<?xml version="1.0" encoding="UTF-8"?>
<rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/"
    xmlns:atom="http://www.w3.org/2005/Atom" xmlns:media="http://search.yahoo.com/mrss/" version="2.0">
    <channel>
        
        <title>
            <![CDATA[ Rajdeep Singh - freeCodeCamp.org ]]>
        </title>
        <description>
            <![CDATA[ Browse thousands of programming tutorials written by experts. Learn Web Development, Data Science, DevOps, Security, and get developer career advice. ]]>
        </description>
        <link>https://www.freecodecamp.org/news/</link>
        <image>
            <url>https://cdn.freecodecamp.org/universal/favicons/favicon.png</url>
            <title>
                <![CDATA[ Rajdeep Singh - freeCodeCamp.org ]]>
            </title>
            <link>https://www.freecodecamp.org/news/</link>
        </image>
        <generator>Eleventy</generator>
        <lastBuildDate>Mon, 18 May 2026 22:34:14 +0000</lastBuildDate>
        <atom:link href="https://www.freecodecamp.org/news/author/officialrajdeepsingh/rss.xml" rel="self" type="application/rss+xml" />
        <ttl>60</ttl>
        
            <item>
                <title>
                    <![CDATA[ How to Use the NixOS Linux Distro – A Tutorial for Developers ]]>
                </title>
                <description>
                    <![CDATA[ NixOS is a Linux distribution based on the Nix package manager and the Nix language. It’s first stable release was in 2013, and it uses a declarative, reproducible system configuration that allows atomic upgrades and rollbacks. The Nix language is a ... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-use-the-nixos-linux-distro-a-tutorial-for-developers/</link>
                <guid isPermaLink="false">6967bce8f1306e271c8038cf</guid>
                
                    <category>
                        <![CDATA[ NixOS ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Nix ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Linux ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Rajdeep Singh ]]>
                </dc:creator>
                <pubDate>Wed, 14 Jan 2026 15:57:28 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/res/hashnode/image/upload/v1768330530946/99ecef9a-4654-4281-9443-2039455c121e.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>NixOS is a Linux distribution based on the Nix package manager and the Nix language. It’s first stable release was in 2013, and it uses a declarative, reproducible system configuration that allows atomic upgrades and rollbacks.</p>
<p>The Nix language is a specialized, purely functional programming language. It’s used by the Nix package manager to build packages and the NixOS operating system for declarative system configuration and software packaging. </p>
<p>Unlike traditional Linux distributions, NixOS utilizes the Nix programming language to describe the entire system, including packages, services, users, networking, and even the bootloader – all of which are defined through a declarative configuration. This approach enables NixOS to generate complete system profiles, allowing for reproducible deployments, atomic upgrades, and easier system rollbacks.</p>
<p>In simpler terms, in NixOS, you can configure your programs, services, and users, and install new system-wide packages or applications directly within the <code>configuration.nix</code> file – which you can then share directly with others.</p>
<p>Also, if anything goes wrong with your current NixOS generation during the system build time, you can roll back to a previous NixOS generation (after switching to a new generation – more on this below).</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1768044273038/62862f39-0706-4611-8a91-0b3ab2839a02.png" alt="NixOS - what it is, and what it is not" class="image--center mx-auto" width="3000" height="1500" loading="lazy"></p>
<p>In this tutorial, I’ll explain in detail what NixOS is, how it works, its benefits, and how to set it up on your machine or laptop in a beginner-friendly way.</p>
<h2 id="heading-table-of-contents"><strong>Table of Contents:</strong></h2>
<ol>
<li><p><a target="_blank" href="https://preview.freecodecamp.org/69416680eb9d6846d92f037e#heading-prerequisites">Prerequisites</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-what-is-a-declarative-configuration">What is a Declarative Configuration?</a></p>
<ul>
<li><p><a class="post-section-overview" href="#heading-why-is-the-declarative-approach-declarative-configuration-used-in-nixos">Why is the Declarative Approach (Declarative Configuration) used in NixOS?</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-what-are-reproducible-systems">What Are Reproducible Systems?</a></p>
</li>
</ul>
</li>
<li><p><a class="post-section-overview" href="#heading-how-does-nixos-work">How Does NixOS Work?</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-what-are-the-benefits-of-using-nixos">What Are the Benefits of Using NixOS?</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-when-would-nixos-not-be-the-best-choice">When Would NixOS Not Be the Best Choice?</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-set-up-nixos-and-the-nix-package-manager-on-your-laptop">How to Set Up NixOS and the Nix Package Manager on Your Laptop</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-install-a-package-in-nixos">How to Install a Package in NixOS?</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-faq">FAQ</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-conclusion">Conclusion</a></p>
</li>
</ol>
<h2 id="heading-prerequisites">Prerequisites</h2>
<p>To work with NixOS and the Nix package manager, there are no specific prerequisites if you have at least a couple years of experience with Ubuntu, Debian, or any other distribution. Having some basic knowledge of the Nix language is a plus.</p>
<h2 id="heading-what-is-a-declarative-configuration">What is a Declarative Configuration?</h2>
<p>In the declarative approach, we use a file (such as a YAML, JSON, or Nix) to describe the configuration for hardware and software components, such as systems, networking, users, boot loader, services (like with systems), and more, in one file.</p>
<p>NixOS uses the <code>configuration.nix</code> file for its declarative configuration. By default, the <code>configuration.nix</code> file looks like this:</p>
<pre><code class="lang-bash"><span class="hljs-comment"># /etc/nixos/configuration.nix</span>

{ config, pkgs, ... }:
{

  imports = [
      ./hardware-configuration.nix
   ];

  boot.loader.systemd-boot.enable = <span class="hljs-literal">true</span>;
  boot.loader.efi.canTouchEfiVariables = <span class="hljs-literal">true</span>;

  networking.hostName = <span class="hljs-string">"nixos"</span>; <span class="hljs-comment"># Define your hostname.</span>

  <span class="hljs-comment"># Enable networking</span>
  networking.networkmanager.enable = <span class="hljs-literal">true</span>;

  <span class="hljs-comment"># Set your time zone.</span>
  time.timeZone = <span class="hljs-string">"Asia/Kolkata"</span>;

  <span class="hljs-comment"># Select internationalisation properties.</span>
  i18n.defaultLocale = <span class="hljs-string">"en_IN"</span>;

  i18n.extraLocaleSettings = {
    LC_ADDRESS = <span class="hljs-string">"en_IN"</span>;
    LC_IDENTIFICATION = <span class="hljs-string">"en_IN"</span>;
    LC_MEASUREMENT = <span class="hljs-string">"en_IN"</span>;
    LC_MONETARY = <span class="hljs-string">"en_IN"</span>;
    LC_NAME = <span class="hljs-string">"en_IN"</span>;
    LC_NUMERIC = <span class="hljs-string">"en_IN"</span>;
    LC_PAPER = <span class="hljs-string">"en_IN"</span>;
    LC_TELEPHONE = <span class="hljs-string">"en_IN"</span>;
    LC_TIME = <span class="hljs-string">"en_IN"</span>;
  };

  <span class="hljs-comment"># Enable the X11 windowing system.</span>
  services.xserver.enable = <span class="hljs-literal">true</span>;

  <span class="hljs-comment"># Enable the GNOME Desktop Environment.</span>
  services.xserver.displayManager.gdm.enable = <span class="hljs-literal">true</span>;
  services.xserver.desktopManager.gnome.enable = <span class="hljs-literal">true</span>;

  <span class="hljs-comment"># remove preinstall or  unused package in gnome</span>
  environment.gnome.excludePackages = with pkgs; [ gnome-tour gnome.gnome-music nixos-render-docs  ];
  services.xserver.excludePackages = with  pkgs; [ xterm ];

  <span class="hljs-comment"># Configure keymap in X11</span>
  services.xserver.xkb = {
    layout = <span class="hljs-string">"us"</span>;
    variant = <span class="hljs-string">""</span>;
  };

  <span class="hljs-comment"># Enable sound with pipewire.</span>
  sound.enable = <span class="hljs-literal">true</span>;
  hardware.pulseaudio.enable = <span class="hljs-literal">false</span>;
  security.rtkit.enable = <span class="hljs-literal">true</span>;
  services.pipewire = {
    <span class="hljs-built_in">enable</span> = <span class="hljs-literal">true</span>;
    alsa.enable = <span class="hljs-literal">true</span>;
    alsa.support32Bit = <span class="hljs-literal">true</span>;
    pulse.enable = <span class="hljs-literal">true</span>;
  };

  <span class="hljs-comment"># Define a user account. Don't forget to set a password with ‘passwd’.</span>
  users.users.officialrajdeepsingh = {
    isNormalUser = <span class="hljs-literal">true</span>;
    description = <span class="hljs-string">"officialrajdeepsingh"</span>;
    extraGroups = [ <span class="hljs-string">"networkmanager"</span> <span class="hljs-string">"wheel"</span> <span class="hljs-string">"docker"</span> ];
    packages = with pkgs; [
      google-chrome
    ];
  };

  <span class="hljs-comment"># Enable automatic login for the user.</span>
  services.xserver.displayManager.autoLogin.enable = <span class="hljs-literal">true</span>;
  services.xserver.displayManager.autoLogin.user = <span class="hljs-string">"officialrajdeepsingh"</span>;

  <span class="hljs-comment"># Workaround for GNOME autologin: https://github.com/NixOS/nixpkgs/issues/103746#issuecomment-945091229</span>
  systemd.services.<span class="hljs-string">"getty@tty1"</span>.<span class="hljs-built_in">enable</span> = <span class="hljs-literal">false</span>;
  systemd.services.<span class="hljs-string">"autovt@tty1"</span>.<span class="hljs-built_in">enable</span> = <span class="hljs-literal">false</span>;


  services.openssh = {
      <span class="hljs-built_in">enable</span> = <span class="hljs-literal">true</span>;
      settings = {
        PasswordAuthentication = <span class="hljs-literal">true</span>;
      };
  };
  system.stateVersion = <span class="hljs-string">"23.05"</span>; <span class="hljs-comment"># Did you read the comment?</span>
}
</code></pre>
<p>You can edit the configuration.nix file to easily enable NGINX and Git using NixOS options. NixOS options let you choose whether to turn features on or off and configure how they should work.</p>
<pre><code class="lang-bash"><span class="hljs-comment"># /etc/nixos/configuration.nix</span>

services.nginx.enable = <span class="hljs-literal">true</span>;
programs.git.enable = <span class="hljs-literal">true</span>;

....
</code></pre>
<p>Every time you modify the <code>configuration.nix</code> file to apply changes to NixOS, you’ll need to build your NixOS using the following command:</p>
<pre><code class="lang-bash">sudo nixos-rebuild switch
</code></pre>
<p>The <code>nixos-rebuild</code> command generates a new NixOS generation based on your configuration file. This <strong>generation</strong> is a complete, immutable snapshot of your system's configuration (packages, services, settings) that’s created every time you run that <code>nixos-rebuild</code> command.</p>
<p>The switch flag helps to build and activate the new generation at the same time, and make it the default boot in NixOS.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1768129947654/a21a336e-277e-4566-8b69-d4574ef86c27.png" alt="NixOS generation list" class="image--center mx-auto" width="992" height="718" loading="lazy"></p>
<p>After a system update, if the new generation isn’t desirable, you can roll back or switch to a previous generation using the <code>nixos-rebuild switch --rollback</code> command. For example, if I’m currently on generation 22, which is the default boot, and I don't like this generation, I can use the <code>rollback</code> flag to switch back to generation 21 in NixOS.</p>
<p>The NixOS rollback feature helps you test new functions and features to see if they work properly when you install new applications or programs on NixOS.</p>
<h3 id="heading-why-is-the-declarative-approach-declarative-configuration-used-in-nixos">Why is the <strong>Declarative Approach (Declarative Configuration) used in NixOS?</strong></h3>
<p>The Declarative Approach is particularly useful because it allows you to share your NixOS configuration file with other developers using GitHub and GitLab. By using the <code>configuration.nix</code> file, other developers can build the same system or machine (making it reproducible).</p>
<p>This approach also helps you manage configurations because all your settings are in one place, making it easy to adjust them at any time.</p>
<h3 id="heading-what-are-reproducible-systems">What Are Reproducible Systems?</h3>
<p>This concept of reproducibility is important. In NixOS, we can achieve reproducibility using the <code>configuration.nix</code> file. For example, when two developers use the same NixOS configuration file on their machines, they can achieve highly reproducible and consistent system setups.</p>
<p>This is one feature that makes NixOS so useful for developers, teams, CI/CD, servers, and DevOps. With NixOS, you can avoid the common issue of "it doesn't work on my machine."</p>
<h2 id="heading-how-does-nixos-work">How Does NixOS Work?</h2>
<p>NixOS works differently compared to traditional Linux distributions. In traditional Linux distros, such as Ubuntu and Debian, you can use the apt command to install a new application or program in your distro, like this:</p>
<pre><code class="lang-bash">sudo apt install git <span class="hljs-comment"># Install git package</span>

sudo apt install nodejs <span class="hljs-comment"># Install node.js package</span>

sudo apt install npm  <span class="hljs-comment"># Install NPM package</span>
</code></pre>
<p>But as we discussed above, NixOS uses a declarative configuration approach that’s immutable, reproducible, and portable.</p>
<p>This means that you can’t install any packages or programs like Git, Chrome, Firefox, Node, Deno, Bun, and so on using the apt, dkpg, or pacman commands (as NixOS uses its own package manager, Nix).</p>
<p>Instead, you edit the <code>configuration.nix</code> file and mention your package and program, such as Node.js, NGINX, or Git in the file and rebuild your NixOS using the nixos-rebuild command.</p>
<pre><code class="lang-bash"><span class="hljs-comment"># /etc/nixos/configuration.nix</span>

services.nginx.enable = <span class="hljs-literal">true</span>;
programs.git.enable = <span class="hljs-literal">true</span>;

environment.systemPackages = [
  pkgs.nodejs_24
];
</code></pre>
<p>Again, this creates a new generation every time you modify the NixOS configuration and run the <code>nixos-rebuild</code> command in your system.</p>
<p>To understand in more detail how NixOS works, check out this <a target="_blank" href="https://nixos.org/guides/how-nix-works/">more in-depth tutorial</a>.</p>
<h2 id="heading-what-are-the-benefits-of-using-nixos">What Are the Benefits of Using NixOS?</h2>
<p>There are many benefits to using NixOS over a traditional distro, some of which I’ve already mentioned. Let’s summarize them here:</p>
<ol>
<li><p><strong>Declarative configuration</strong>: All settings for your system, including programs, applications, and services, are written in a single configuration file rather than being installed manually.</p>
</li>
<li><p><strong>Instant rollbacks</strong>: There's no need to worry about breaking your system. If something goes wrong during an update, you can easily revert to the previous version.</p>
</li>
<li><p><strong>Safe updates</strong>: Updates either complete successfully or don’t apply at all, ensuring your system never ends up in a half-broken state.</p>
</li>
<li><p><strong>Reproducible systems</strong>: With the same configuration, you can recreate the same system every time, eliminating issues like "it doesn’t work on my machine."</p>
</li>
<li><p><strong>No dependency conflicts</strong>: Multiple versions of applications can coexist without issues, allowing different programs such as Node.js and Python to operate together seamlessly.</p>
</li>
<li><p><strong>Extensive package ecosystem</strong>: The Nix Packages collection comprises thousands of up-to-date packages maintained by the NixOS community.</p>
</li>
<li><p><strong>Setting up a new machine</strong>: Copy your configuration file, rebuild the system, and complete the setup in just a few minutes, whether for a laptop or a server.</p>
</li>
<li><p><strong>Immutable system</strong>: The design of an immutable system keeps core system paths unchanged. This prevents accidental alterations and enhances reliability, as core components become read-only and cannot be modified after the initial build.</p>
</li>
</ol>
<h2 id="heading-when-would-nixos-not-be-the-best-choice">When Would NixOS Not Be the Best Choice?</h2>
<p>There are various situations where NixOS may not be the best choice for you. Here are some of the main issues:</p>
<ol>
<li><p>NixOS has a steep learning curve, particularly for beginner and intermediate developers.</p>
</li>
<li><p>It doesn’t offer a simple one-click installation solution for applications and programs on your machine or laptop.</p>
</li>
<li><p>You can’t install system-wide applications or programs without editing the NixOS configuration files and rebuilding the system.</p>
</li>
<li><p>NixOS lacks a larger community and readily available tutorials compared to Ubuntu, and its documentation is not very beginner-friendly – so you may need to rely on your own resources.</p>
</li>
</ol>
<h2 id="heading-how-to-set-up-nixos-and-the-nix-package-manager-on-your-laptop">How to Set Up NixOS and the Nix Package Manager on Your Laptop</h2>
<p>Now we’re ready to dive in and set up NixOS. But what you need to install depends on your operating system:</p>
<ul>
<li><p>On macOS or Windows, you’ll install Nix, the package manager. This lets you use Nix to install and manage software on your existing operating system. You <strong>don’t</strong> install NixOS itself on macOS or Windows.</p>
</li>
<li><p>On Linux, installing NixOS means installing a new OS (it’s like installing Ubuntu or Debian).</p>
</li>
</ul>
<p>Because NixOS is a full operating system, you’ll need to install it on a fresh machine or partition, which will typically erase existing data during installation unless you set up dual-booting.</p>
<p>And remember, while NixOS is a Linux distribution, it works differently (than Ubuntu or Debian, for example) because the entire system is configured declaratively using Nix.</p>
<h3 id="heading-install-nix-package-manager">Install Nix Package Manager</h3>
<p>The following command helps you to install the Nix language and the Nix Package Manager on macOS and Windows (via WSL).</p>
<pre><code class="lang-bash"><span class="hljs-comment"># Windows: Multi-user installation (recommended)</span>
sh &lt;(curl --proto <span class="hljs-string">'=https'</span> --tlsv1.2 -L https://nixos.org/nix/install) --daemon

<span class="hljs-comment"># Windows: Single-user installation</span>
sh &lt;(curl --proto <span class="hljs-string">'=https'</span> --tlsv1.2 -L https://nixos.org/nix/install) --no-daemon

<span class="hljs-comment"># MacOS:</span>
sh &lt;(curl --proto <span class="hljs-string">'=https'</span> --tlsv1.2 -L https://nixos.org/nix/install)
</code></pre>
<p>If you’re a newcomer, before running the command I recommend <a target="_blank" href="https://nixos.org/download/#nix-install-macos">watching this tutorial on YouTube</a> and checking out the official documentation.</p>
<h3 id="heading-install-nixos">Install NixOS</h3>
<p>You can install the NixOS distro with a Linux command. Before proceeding, make sure you meet the following requirements:</p>
<ul>
<li><p>A USB drive (8 GB or more)</p>
</li>
<li><p>A second computer (to create the USB)</p>
</li>
<li><p>A backup of your data (installation can erase the disk)</p>
</li>
<li><p>An internet connection (Wi-Fi or Ethernet)</p>
</li>
</ul>
<p>There are multiple steps for installing NixOS on your machine or laptop. You can check out the following tutorial, which describes in detail how you can install NixOS on your machine very easily.</p>
<div class="embed-wrapper">
        <iframe width="560" height="315" src="https://www.youtube.com/embed/N39_cg8QyT4" style="aspect-ratio: 16 / 9; width: 100%; height: auto;" title="YouTube video player" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen="" loading="lazy"></iframe></div>
<p> </p>
<h2 id="heading-how-to-install-a-package-in-nixos">How to Install a Package in NixOS</h2>
<p>NixOS has a large registry of active packages, with 120,000 packages available. Every package you install from the stable channel is built reproducibly and reviewed by the Nix community, so you shouldn’t encounter any issues.</p>
<p>Installing a new package on NixOS using the Nix Package Manager is quite simple. First, visit the <a target="_blank" href="https://search.nixos.org/packages">NixOS Packages Search</a> site.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1767107353823/ae26031b-5395-4e36-87ed-d7b4cdd2be9f.png" alt="Search package on NixOS packages website" class="image--center mx-auto" width="1920" height="961" loading="lazy"></p>
<p>Then just search for the package that you’re looking for. In our case, we’ll search for Neovim – and then just type the package name in the search input and hit enter.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1768142068118/34333980-65ae-4231-a7a2-9919e3135bcd.png" alt="Search for neovim on the packages website" class="image--center mx-auto" width="1920" height="961" loading="lazy"></p>
<p>Copy the resulting code it shows, and paste it into your <code>configuration.nix</code> file:</p>
<pre><code class="lang-bash"><span class="hljs-comment"># /etc/nixos/configuration.nix</span>

environment.systemPackages = [
  pkgs.neovim <span class="hljs-comment"># add inside file.</span>
];
</code></pre>
<p>Then rebuild your NixOS using the <code>sudo nixos-rebuild switch</code> command. Remember that you’ll need to do this whenever you make changes to the <code>configuration.nix</code> file.</p>
<h3 id="heading-demo-how-to-install-neovim-in-nixos">Demo (How to Install Neovim in NixOS)</h3>
<p><a target="_blank" href="https://www.youtube.com/watch?v=wFP9CbaeMe0"><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1768146169478/d941876f-abf0-49f6-b1cc-182c608c094e.gif" alt="Screenshot of a text editor displaying a configuration file with commented instructions and settings for installing software packages like Firefox on a system. The interface includes options such as Exit, Save, and Execute at the bottom. Copyright by Low Orbit Flux" class="image--center mx-auto" width="640" height="360" loading="lazy"></a></p>
<h2 id="heading-faq">FAQ</h2>
<h3 id="heading-is-nixos-only-for-advanced-users">Is NixOS only for advanced users?</h3>
<p>No, NixOS can be a great fit for anyone. Due to its steeper learning curve, beginners may struggle at first – but once you understand it, I bet you’ll love it.</p>
<h3 id="heading-why-is-the-nix-language-so-weird">Why is the Nix language so weird?</h3>
<p>Nix is purely functional and is designed for reproducible builds. It’s not a general-purpose language and is rather a configuration language. You’ll only need 10–15% of the language for daily use.</p>
<h3 id="heading-how-is-nixos-different-from-ubuntu-arch">How is NixOS different from Ubuntu / Arch?</h3>
<p>Let’s compare some important NixOS features with other distros:</p>
<div class="hn-table">
<table>
<thead>
<tr>
<td>Feature</td><td>Ubuntu / Arch</td><td>NixOS</td></tr>
</thead>
<tbody>
<tr>
<td>Install apps</td><td>Manual</td><td>Declarative</td></tr>
<tr>
<td>Rollbacks</td><td>No</td><td>Yes</td></tr>
<tr>
<td>System config</td><td>Spread everywhere</td><td>One file</td></tr>
<tr>
<td>Reproducibility</td><td>Hard</td><td>Built-in</td></tr>
<tr>
<td>Learning curve</td><td>Low</td><td>High</td></tr>
</tbody>
</table>
</div><h3 id="heading-can-i-use-nixos-for-development">Can I use NixOS for development?</h3>
<p>Yes, NixOS is an excellent distro for:</p>
<ul>
<li><p>Frontend development (Node, Bun, Deno)</p>
</li>
<li><p>Backend development (Go, Rust, Python)</p>
</li>
<li><p>Consistent development environments across machines</p>
</li>
<li><p>CI/CD reproducibility</p>
</li>
</ul>
<h3 id="heading-is-nixos-good-for-daily-use">Is NixOS good for daily use?</h3>
<p>Yes – NixOS has an initial learning phase that can be difficult, but once you get past it, you can use NixOS on your work laptops, servers, home PCs, and development machines.</p>
<h3 id="heading-should-i-learn-nixos-as-a-beginner-developer">Should I learn NixOS as a beginner developer?</h3>
<p>To be honest, if you want quick results, NixOS may not be for you. But if you're aiming for long-term mastery, you will definitely like NixOS.</p>
<h3 id="heading-does-nixos-work-on-macos-and-windows">Does NixOS work on macOS and Windows?</h3>
<p>Yes, you can use Nix and the Nix Package Manager on macOS and Windows, and they work well. You can install and package software using the Nix configuration file as mentioned in this tutorial.</p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>I think that NixOS is the best Linux distro – but it’s not super beginner-friendly. Still, I prefer it because it’s a powerful and reliable distro that’s designed for users who value control, reproducibility, and safety.</p>
<p>Managing the entire system through declarative configuration enables consistent setups, safe upgrades, and easy rollbacks. This makes it especially suitable for developers, DevOps engineers, and infrastructure-focused teams.</p>
<p>Just keep in mind that NixOS is not for everyone. Its learning curve and configuration-driven workflow can be steep for beginners, casual users, or those who want quick, click-and-install convenience. So check it out and decide if it’s right for you and your team.</p>
<p>To Learn more beginner tutorials on NixOS, check out <a target="_blank" href="https://medium.com/thenixos">the NixOS</a> publication on Medium.</p>
<ul>
<li><p><a target="_blank" href="https://medium.com/thenixos/what-is-declarative-configuration-in-nixos-understanding-declarative-vs-imperative-approaches-d24d4d144df6">What is Declarative Configuration in NixOS? Understanding Declarative vs Imperative Approaches</a></p>
</li>
<li><p><a target="_blank" href="https://medium.com/thenixos/understand-the-difference-between-home-manager-vs-nix-flake-in-nixos-0511dc8c1a93"><strong>Understand the difference between Home Manager vs Nix Flake in NixOS?</strong></a></p>
</li>
<li><p><a target="_blank" href="https://medium.com/thenixos/why-did-i-choose-nixos-and-what-are-the-advantages-and-disadvantages-of-using-nixos-afaaf95f7d8e">Why did I choose NixOS, and what are the advantages and disadvantages of using NixOS?</a></p>
</li>
</ul>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Create Documentation with docs.page – A Beginner's Tutorial ]]>
                </title>
                <description>
                    <![CDATA[ One of the most tedious tasks for every startup, company, and open-source project is often building and managing documentation – especially for medium to large-scale documentation websites. docs.page is an open-source documentation tool that helps yo... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-create-documentation-with-docspage/</link>
                <guid isPermaLink="false">681a558b9791d0e469b84519</guid>
                
                    <category>
                        <![CDATA[ documentation ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Beginner Developers ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Rajdeep Singh ]]>
                </dc:creator>
                <pubDate>Tue, 06 May 2025 18:31:39 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/res/hashnode/image/upload/v1746471569068/23f70d3e-a76e-4287-a6a9-579c23a4fcb2.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>One of the most tedious tasks for every startup, company, and open-source project is often building and managing documentation – especially for medium to large-scale documentation websites.</p>
<p><a target="_blank" href="http://docs.page"><strong>docs.page</strong></a> is an open-source documentation tool that helps you create instant, fast, beautiful, and responsive documentation websites with minimal configuration. It is an open-source project developed by Invertase, a company known for creating developer tools and SDKs.</p>
<p>docs.page is designed to streamline the process of publishing documentation by sourcing content directly from public GitHub repositories.</p>
<h3 id="heading-key-features">Key Features:</h3>
<ul>
<li><p>Zero configuration: you create a 'docs.json' file and a 'docs' directory. Inside the docs directory, you can create files using the .mdx extension, and docs.page will generate your documentation site.</p>
</li>
<li><p>Customizable: docs.page allows you to add your logo, social links, theme, analytics, navigation, and more through a simple configuration file.</p>
</li>
<li><p>Live previews: enables viewing of documentation for any branch, pull request, or specific commit, facilitating real-time collaboration and review.</p>
</li>
<li><p>Hot reload: the Hot Reload feature provides real-time previews of documentation changes while editing Markdown (.mdx) files. This feature enhances the local development workflow, enabling instant updates without manual refreshes or rebuilds.</p>
</li>
<li><p>GitHub bot integration: provides a GitHub bot that automatically generates URLs for pull request documentation previews.</p>
</li>
<li><p>MDX support: you can write documentation in Markdown to utilize MDX, which enables you to use React components, such as tabs, Cards, Tweets, and Steps, directly within your Markdown file.</p>
</li>
<li><p>Search functionality: integrates with DocSearch to offer full-text search capabilities within your documentation.</p>
</li>
<li><p>Responsive resign: ensures that your documentation is accessible and visually appealing across a range of devices and screen sizes.</p>
</li>
<li><p>Dark/light mode: offers theme customization to switch between dark and light modes.</p>
</li>
<li><p>Code block highlighting: provides syntax highlighting and content copying features for code blocks.</p>
</li>
</ul>
<p>Check out the code <a target="_blank" href="https://github.com/officialrajdeepsingh/docs-page-demo">available in my GitHub repository</a>.</p>
<h2 id="heading-table-of-contents"><strong>Table of Contents:</strong></h2>
<ul>
<li><p><a class="post-section-overview" href="#heading-how-does-docspage-work">How Does</a> <a target="_blank" href="http://docs.page">docs.page</a> <a class="post-section-overview" href="#heading-how-does-docspage-work">Work?</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-enable-live-preview-in-docspage">How to Enable Live Preview in</a> <a target="_blank" href="http://docs.page">docs.page</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-configure-docspage">How to Configure</a> <a target="_blank" href="http://docs.page">docs.page</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-use-pre-built-components-in-docspage">How to Use Pre-built Components in</a> <a target="_blank" href="http://docs.page">docs.page</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-diagnose-errors-in-docspage">How to Diagnose Errors in</a> <a target="_blank" href="http://docs.page">docs.page</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-use-frontmatter">How to Use Frontmatter</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-add-assets-to-your-docs">How to Add Assets to Your Docs</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-publish-your-documentation-website">How to Publish Your Documentation Website</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-can-you-live-preview-your-upcoming-changes-to-your-documentation-website">How can you live preview your upcoming changes to your documentation website?</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-conclusion">Conclusion</a></p>
</li>
</ul>
<h2 id="heading-how-does-docspage-work">How Does docs.page Work?</h2>
<p>You can easily start creating your documentation page using the <a target="_blank" href="https://use.docs.page/cli">docs.page CLI</a>. It helps you set up a local documentation project by running the following command:</p>
<pre><code class="lang-bash">pnpm dlx @docs.page/cli init docs.page
</code></pre>
<p>The command output appears as follows:</p>
<pre><code class="lang-bash">pnpm dlx @docs.page/cli init docs.page
? Are you sure you want to setup and install docs.page <span class="hljs-keyword">in</span> /home/officialrajdeepsingh/medium/docs.page? yes
Files created:
 - docs.json: Configuration file <span class="hljs-keyword">for</span> your documentation site
 - docs/index.mdx: The home page of your documentation site
 - docs/next-steps.mdx: A page to <span class="hljs-built_in">help</span> you get started with docs.page

Initialization complete. To preview your documentation site, vist https://docs.page/preview <span class="hljs-keyword">in</span> your browser.
</code></pre>
<p>After creating your project with the docs.page CLI, your project structure should appear as follows:</p>
<pre><code class="lang-bash">.
├── docs
│   ├── index.mdx
│   └── next-steps.mdx
└── docs.json

2 directories, 3 files
</code></pre>
<p>The <code>docs</code> folder contains the Markdown file for your documentation, and the <code>docs.json</code> file includes the configuration for your website, such as the header, sidebar, logo, theme, and other settings.</p>
<h2 id="heading-how-to-enable-live-preview-in-docspage">How to Enable Live Preview in docs.page</h2>
<p>You can set up live preview of your local documentation in real-time in the browser – but it's a little different: you don't need to run any development commands on your laptop or machine.</p>
<p>To open the live preview of your local documentation, first visit <a target="_blank" href="https://docs.page">https://docs.page</a> and click the <strong>Local Preview</strong> button.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1745671253187/c2e6ce0b-aedb-4e7e-b680-68e590fc4018.png" alt="Live preview your local documentation in real-time directly in the browser." class="image--center mx-auto" width="1920" height="961" loading="lazy"></p>
<p>Next, select the documentation project on your laptop or machine and click the "<strong>Select Directory</strong>" button.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1745664832273/fc03e2d5-02c0-4bce-b40c-a2599ef72195.png" alt=" click on the &quot;Select Directory&quot; button and select directory" class="image--center mx-auto" width="1920" height="961" loading="lazy"></p>
<p>After clicking the "Select Directory" button, a new window will open depending on your operating system. Its UI may appear different. Then you need to select the project.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1745664861969/39a31043-22e8-4d6b-a883-19be0a59ca4d.png" alt="Select the Directory" class="image--center mx-auto" width="1165" height="672" loading="lazy"></p>
<p>After selecting the folder, you will see the following alert message in the browser (“Let site view files?”). To view the live preview of your documentation website, click the "View files" button.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1745664971450/8ec6f635-a2e4-401e-8fa8-cf49c4e06b9a.png" alt="Click on View files button" class="image--center mx-auto" width="1917" height="706" loading="lazy"></p>
<p>Now you can see a local live preview of the documentation website in the browser, and any changes you make locally will instantly reflect in the browser. By default, your documentation website should appear as follows:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1745674174727/dd4f1820-ce04-4244-b395-b055bb8d236a.png" alt="Live preview your documentation website in the browser" class="image--center mx-auto" width="1920" height="961" loading="lazy"></p>
<p>Next, you’ll learn about configuring the Logo, Theme, Header, Social Links, Sidebar, SEO, search, and more on your docs. You’ll also learn how to use the pre-built components, Front Matter, and assets on docs.page, and finally, how to deploy your documentation website.</p>
<h2 id="heading-how-to-configure-docspage">How to Configure docs.page</h2>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1745834873075/6c9dd17b-20e2-40dc-87a1-39cc27cf9a20.png" alt="Configure docs.page in the docs.json file." class="image--center mx-auto" width="1915" height="1046" loading="lazy"></p>
<p>The <code>docs.json</code> file is the primary file for configuring your documentation. Below is a <a target="_blank" href="https://use.docs.page/configuration">list of all available configuration options</a>, which you can use to modify the logos, theme, analytics, and more on your docs.</p>
<h3 id="heading-properties"><strong>Properties</strong></h3>
<ul>
<li><p>Basic properties</p>
</li>
<li><p>Logo</p>
</li>
<li><p>Theme</p>
</li>
<li><p>Header</p>
</li>
<li><p>Anchors</p>
</li>
<li><p>Social Links</p>
</li>
<li><p>SEO</p>
</li>
<li><p>Variables</p>
</li>
<li><p>Search</p>
</li>
<li><p>Scripts</p>
</li>
<li><p>Content</p>
</li>
<li><p>Tabs</p>
</li>
<li><p>Sidebar</p>
</li>
</ul>
<p>There’s so much you can configure using docs.page – but in this tutorial, we’ll focus on some of the most important options:</p>
<ul>
<li><p><a class="post-section-overview" href="#heading-properties">Properties</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-basic-properties">Basic Properties</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-logo">Logo</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-theme">Theme</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-header">Header</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-social-links">Social Links</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-seo">SEO</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-search">Search</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-tabs">Tabs</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-sidebar">Sidebar</a></p>
</li>
</ul>
<h3 id="heading-basic-properties">Basic Properties</h3>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1745915741849/802ba7e3-ae6b-4628-856f-0d78aa4e1bfc.png" alt="Basic properties such as name, description and favicon" class="image--center mx-auto" width="1677" height="414" loading="lazy"></p>
<p>docs.page includes basic common properties, such as name, description, and favicon, which is very important for SEO.</p>
<ul>
<li><p>name (string): The name of your project. It appears in the header and is used for things like SEO metadata.</p>
</li>
<li><p>description (string): A summary of your project. This is used in meta tags and social preview images.</p>
</li>
<li><p>favicon (string | Favicon object): Specifies the favicon shown in the browser tab. You can provide either a single string URL or use a Favicon object to define different icons for light and dark modes:</p>
<ul>
<li><p>light (string): URL for the favicon in light mode.</p>
</li>
<li><p>dark (string): URL for the favicon in dark mode.</p>
</li>
</ul>
</li>
</ul>
<pre><code class="lang-json"><span class="hljs-comment">// docs.json</span>
{
  <span class="hljs-attr">"name"</span>: <span class="hljs-string">"Docs.page"</span>,
  <span class="hljs-attr">"description"</span>: <span class="hljs-string">"Ship documentation, like you ship code"</span>,
  <span class="hljs-attr">"favicon"</span>: <span class="hljs-string">"https://static.invertase.io/assets/docs.page/docs-page-logo.png"</span>,
   # or
  <span class="hljs-attr">"favicon"</span>: {
    <span class="hljs-attr">"light"</span>: <span class="hljs-string">"https://cdn-icons-png.flaticon.com/24/9664/9664027.png"</span>,
    <span class="hljs-attr">"dark"</span>: <span class="hljs-string">"https://cdn-icons-png.flaticon.com/24/9643/9643115.png"</span>
  }
}
</code></pre>
<h3 id="heading-logo">Logo</h3>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1745750725706/1f3f2775-91d8-4365-88b4-1a43160d12c3.png" alt="Configure the logo for your documentation" class="image--center mx-auto" width="1866" height="93" loading="lazy"></p>
<p>Now it’s time to configure the logo for your documentation, which will appear in the header and be used for social preview images.</p>
<p>The minimum height of the logo must be 24px. You can provide URLS for both a light and a dark logo. If you only provide a light or dark logo, and it doesn't work, you may experience issues where your logo doesn't appear on the website when toggling the theme.</p>
<p>You can add the logo to the documentation in two ways:</p>
<ul>
<li>First way:</li>
</ul>
<pre><code class="lang-json"><span class="hljs-comment">// docs.json</span>
{
  <span class="hljs-attr">"name"</span>: <span class="hljs-string">"My Docs"</span>,
  <span class="hljs-attr">"logo"</span>: <span class="hljs-string">"https://cdn-icons-png.flaticon.com/24/2702/2702154.png"</span>,
}
</code></pre>
<ul>
<li>Second way:</li>
</ul>
<pre><code class="lang-json"><span class="hljs-comment">// docs.json</span>
{
  <span class="hljs-attr">"name"</span>: <span class="hljs-string">"My Docs"</span>,
  <span class="hljs-attr">"logo"</span>: {
    <span class="hljs-attr">"light"</span>: <span class="hljs-string">"https://cdn-icons-png.flaticon.com/24/2702/2702154.png"</span>,
    <span class="hljs-attr">"dark"</span>: <span class="hljs-string">"https://cdn-icons-png.flaticon.com/24/2702/2702172.png"</span>
  }
}
</code></pre>
<h3 id="heading-theme">Theme</h3>
<p>Configuring the theme in your documentation is easy. If you don’t provide a theme, the default theme will be used in your documentation.</p>
<p>docs.page includes a theme property in docs.json, which holds a Theme object as its value with the properties <code>defaultTheme</code>, <code>primary</code>, <code>primaryLight</code>, <code>backgroundLight</code>, and <code>backgroundDark</code>.</p>
<ul>
<li><p><code>defaultTheme</code>: You can select a theme, dark or light.</p>
</li>
<li><p><code>primary</code>: The primary colour is used for links, buttons, and other interactive elements.</p>
</li>
<li><p><code>primaryLight</code>: The <code>primaryLight</code> colour option is used in light mode. If your primary light option is not specified in the <code>docs.json</code> file, then the primary colour will be used.</p>
</li>
<li><p><code>primaryDark</code>: The <code>primaryDark</code> colour option is used in dark mode. If your <code>primaryDark</code> option is not specified in the <code>docs.json</code> file, then the primary color will be used.</p>
</li>
<li><p><code>backgroundLight</code>: The <code>backgroundLight</code> option is used to specify the background color of your documentation in light mode.</p>
</li>
<li><p><code>backgroundDark</code>: The <code>backgroundDark</code> option is used to specify the background color of your documentation in dark mode.</p>
</li>
</ul>
<pre><code class="lang-json"><span class="hljs-comment">// docs.json</span>
{
  <span class="hljs-attr">"theme"</span>: {
    <span class="hljs-attr">"defaultTheme"</span>: <span class="hljs-string">"dark"</span>,
    <span class="hljs-attr">"primary"</span>: <span class="hljs-string">"#de40eb"</span>,
    <span class="hljs-attr">"primaryLight"</span>: <span class="hljs-string">"#BFA213"</span>,
    <span class="hljs-attr">"backgroundLight"</span>: <span class="hljs-string">"#e0cfff"</span>,
    <span class="hljs-attr">"backgroundDark"</span>: <span class="hljs-string">"#00101f"</span>
  },
}
</code></pre>
<h3 id="heading-header">Header</h3>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1745912602721/2430492a-8d58-4f4b-bc4c-e22f8cb7b52f.png" alt="configuration of the header in your documentation" class="image--center mx-auto" width="1901" height="638" loading="lazy"></p>
<p>Configuring the header in your documentation includes the following properties: <code>showName</code>, <code>showThemeToggle</code>, <code>showGitHubCard</code>, and links.</p>
<ul>
<li><p><code>showName</code>: The <code>showName</code> option displays the documentation name next to the logo in the header and defaults it is true.</p>
</li>
<li><p><code>showThemeToggle</code>: The <code>showThemeToggle</code> option displays the theme toggle button in the header (and defaults to true).</p>
</li>
<li><p><code>showGitHubCard</code>: The <code>showGitHubCard</code> option displays the GitHub card in the header and defaults to true.</p>
</li>
<li><p>Links: The links option contains an array of Link objects to display a navigation in the header of your documentation.</p>
</li>
</ul>
<pre><code class="lang-json"><span class="hljs-comment">// docs.json</span>
{
  <span class="hljs-attr">"header"</span>: {
    <span class="hljs-attr">"showName"</span>: <span class="hljs-literal">false</span>,
    <span class="hljs-attr">"showGitHubCard"</span>: <span class="hljs-literal">false</span>,
    <span class="hljs-attr">"links"</span>: [
      {
        <span class="hljs-attr">"title"</span>: <span class="hljs-string">"GitHub"</span>,
        <span class="hljs-attr">"href"</span>: <span class="hljs-string">"https://github.com/officialrajdeepsingh/docs-page-demo"</span>
      },
      {
        <span class="hljs-attr">"title"</span>: <span class="hljs-string">"X"</span>,
        <span class="hljs-attr">"href"</span>: <span class="hljs-string">"https://x.com/Official_R_deep"</span>
      },
      {
        <span class="hljs-attr">"title"</span>: <span class="hljs-string">"Linkedin"</span>,
        <span class="hljs-attr">"href"</span>: <span class="hljs-string">"https://www.linkedin.com/in/officalrajdeepsingh"</span>
      }
    ]
  }
}
</code></pre>
<h3 id="heading-social-links">Social Links</h3>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1745910990442/f48a4b34-d20f-4155-b19a-d8af8a202801.png" alt="configuration of the social links in your documentation" class="image--center mx-auto" width="1920" height="961" loading="lazy"></p>
<p>The social option contains an object of key-value pairs where the key represents the social platform and the value corresponds to the username or ID. Here’s how you can add them:</p>
<pre><code class="lang-json"><span class="hljs-comment">// docs.json</span>
{
  <span class="hljs-attr">"social"</span>: {
    <span class="hljs-attr">"github"</span>: <span class="hljs-string">"officialrajdeepsingh/docs-page-demo"</span>,
    <span class="hljs-attr">"x"</span>: <span class="hljs-string">"@Official_R_deep"</span>,
    <span class="hljs-attr">"linkedin"</span>: <span class="hljs-string">"officalrajdeepsingh"</span>
  }
}
</code></pre>
<h3 id="heading-seo">SEO</h3>
<p>The SEO option configures the SEO settings for your documentation. The noindex option tells search engines not to index your documentation, and it defaults to false.</p>
<pre><code class="lang-json"><span class="hljs-comment">// docs.json</span>
{
  noindex: <span class="hljs-literal">true</span>
}
</code></pre>
<h3 id="heading-search">Search</h3>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1745909928336/a85c4f21-712d-4096-be08-15d605668a68.png" alt="configuration of the search in your documentation" class="image--center mx-auto" width="1920" height="961" loading="lazy"></p>
<p>To enable search functionality on your documentation site, you can integrate Algolia DocSearch by configuring the docsearch object in your <code>docs.json</code> file like this:</p>
<pre><code class="lang-json"><span class="hljs-comment">// docs.json</span>
{
 <span class="hljs-attr">"search"</span>: {
    <span class="hljs-attr">"docsearch"</span>: {
      <span class="hljs-attr">"appId"</span>: <span class="hljs-string">"YOUR_APP_ID"</span>,
      <span class="hljs-attr">"apiKey"</span>: <span class="hljs-string">"YOUR_API_KEY"</span>,
      <span class="hljs-attr">"indexName"</span>: <span class="hljs-string">"YOUR_INDEX_NAME"</span>
    }
  }
}
</code></pre>
<h3 id="heading-tabs">Tabs</h3>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1745909877477/9248bfa7-ed16-4d2a-9d2a-6624d4690123.png" alt="configuration of the tab in your documentation" class="image--center mx-auto" width="1920" height="961" loading="lazy"></p>
<p>Tabs are an array of objects displayed at the top of your documentation website.</p>
<h4 id="heading-properties-1">Properties</h4>
<p>Each Tab object includes the following properties:</p>
<ul>
<li><p>id (string, required): A unique identifier for the tab.</p>
</li>
<li><p>title (string, required): The text label displayed on the tab.</p>
</li>
<li><p>href (string, required): The URL to navigate to when the tab is clicked.</p>
</li>
<li><p>locale (string, optional): If set, this tab is displayed only when viewing documentation for the specified locale.</p>
</li>
</ul>
<p>Here’s an example of a couple tabs:</p>
<pre><code class="lang-json">{
  <span class="hljs-attr">"tabs"</span>: [
    {
      <span class="hljs-attr">"id"</span>: <span class="hljs-string">"root"</span>,
      <span class="hljs-attr">"title"</span>: <span class="hljs-string">"Documentation"</span>,
      <span class="hljs-attr">"href"</span>: <span class="hljs-string">"/"</span>
    },
    {
      <span class="hljs-attr">"id"</span>: <span class="hljs-string">"components"</span>,
      <span class="hljs-attr">"title"</span>: <span class="hljs-string">"Components"</span>,
      <span class="hljs-attr">"href"</span>: <span class="hljs-string">"/components"</span>
    }
  ],
}
</code></pre>
<h3 id="heading-sidebar">Sidebar</h3>
<p>To display the sidebar on your website, you can configure or define it in the <code>docs.json</code> file your documentation, which will appear in the sidebar of your site.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1745911338226/f3404a5d-b715-4e26-97c9-ce8169fe8d6b.png" alt="configuration of the sidebar in your documentation" class="image--center mx-auto" width="1920" height="961" loading="lazy"></p>
<p>Essentially, a sidebar is a list of links that appears on the side of your documentation. You can organize links using groups and pages by providing an array of sidebar objects.</p>
<h4 id="heading-options">Options:</h4>
<ul>
<li><p>pages: The pages option takes a list of page links to display in the sidebar. It accepts the following options:</p>
<ul>
<li><p>title (required): The title of the sidebar item.</p>
</li>
<li><p>href (required): The URL to link to when the sidebar item is clicked.</p>
</li>
<li><p>icon (optional): The icon to display next to the sidebar item.</p>
</li>
</ul>
</li>
</ul>
<ul>
<li><p>group (string): The title of the group under which the sidebar item will be displayed. If not provided, the item will appear at the top level of the sidebar.</p>
</li>
<li><p>href (string): The URL the sidebar item will link to when clicked.</p>
</li>
<li><p>icon (string): The name of the icon to display next to the sidebar item.</p>
</li>
<li><p>tab (string): If set, the sidebar item will only be shown when a specific tab (matching the provided tab ID) is active.</p>
</li>
</ul>
<pre><code class="lang-json"><span class="hljs-comment">// docs.json</span>
{
<span class="hljs-attr">"sidebar"</span>: [
    {
      <span class="hljs-attr">"pages"</span>: [
        {
          <span class="hljs-attr">"title"</span>: <span class="hljs-string">"Overview"</span>,
          <span class="hljs-attr">"href"</span>: <span class="hljs-string">"/"</span>,
          <span class="hljs-attr">"icon"</span>: <span class="hljs-string">"book"</span>
        },
        {
          <span class="hljs-attr">"title"</span>: <span class="hljs-string">"Configuration"</span>,
          <span class="hljs-attr">"href"</span>: <span class="hljs-string">"/configuration"</span>,
          <span class="hljs-attr">"icon"</span>: <span class="hljs-string">"gear"</span>
        }
      ]
    },
    {
      <span class="hljs-attr">"group"</span>: <span class="hljs-string">"Components"</span>,
      <span class="hljs-attr">"icon"</span>: <span class="hljs-string">"grip"</span>,
      <span class="hljs-attr">"pages"</span>: [
        {
          <span class="hljs-attr">"title"</span>: <span class="hljs-string">"Getting Started"</span>,
          <span class="hljs-attr">"href"</span>: <span class="hljs-string">"/components"</span>,
          <span class="hljs-attr">"icon"</span>: <span class="hljs-string">"rocket"</span>
        },
        {
          <span class="hljs-attr">"title"</span>: <span class="hljs-string">"Accordion"</span>,
          <span class="hljs-attr">"href"</span>: <span class="hljs-string">"/components/accordion"</span>,
          <span class="hljs-attr">"icon"</span>: <span class="hljs-string">"square-caret-down"</span>
        },
        {
          <span class="hljs-attr">"title"</span>: <span class="hljs-string">"Callouts"</span>,
          <span class="hljs-attr">"href"</span>: <span class="hljs-string">"/components/callouts"</span>,
          <span class="hljs-attr">"icon"</span>: <span class="hljs-string">"bullhorn"</span>
        },
        {
          <span class="hljs-attr">"title"</span>: <span class="hljs-string">"Cards"</span>,
          <span class="hljs-attr">"href"</span>: <span class="hljs-string">"/components/cards"</span>,
          <span class="hljs-attr">"icon"</span>: <span class="hljs-string">"square-full"</span>
        }
      ]
    }
  ]
}
</code></pre>
<p>If you want to learn more about this, check out the <a target="_blank" href="https://use.docs.page/configuration#sidebar">documentation here</a>.</p>
<h2 id="heading-how-to-use-pre-built-components-in-docspage">How to Use Pre-built Components in docs.page</h2>
<p>docs.page comes with <a target="_blank" href="https://use.docs.page/components">15 pre-built components</a>, so you don't need to import components into your MDX file. You can use them directly in your MDX file.</p>
<p>In the following example, I’m using the Info Callout component directly within the MDX file, without importing it.</p>
<pre><code class="lang-markdown"><span class="hljs-section">// index.mdx
---</span>
title: Welcome to docs.page!
<span class="hljs-section">description: Get started with docs.page
---</span>

Welcome to docs.page! The init command you just ran has created a basic file struture in your project to help you get started.

<span class="hljs-section">## Walkthrough</span>

<span class="hljs-section">### Configuration</span>

In the root of your directory a new <span class="hljs-code">`docs.json`</span> file has been created. This file is used to configure your documentation site. You can customize the name, description, and sidebar, theme, logos and more using this file.


<span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">Info</span>&gt;</span></span>Here's a basic example of what the file looks like: <span class="xml"><span class="hljs-tag">&lt;/<span class="hljs-name">Info</span>&gt;</span></span>
</code></pre>
<h2 id="heading-how-to-diagnose-errors-in-docspage">How to Diagnose Errors in docs.page</h2>
<p>If you encounter any errors on your documentation website, you can view all the errors by clicking the diagnostics button.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1745863994785/16ec2f9a-86e6-4808-abdb-73513a54d428.png" alt="diagnosing the error in docs.page" class="image--center mx-auto" width="1920" height="961" loading="lazy"></p>
<h2 id="heading-how-to-use-frontmatter">How to Use Frontmatter</h2>
<p>Front matter is a block of YAML placed at the beginning of a Markdown file, enclosed between triple-dash <code>(---)</code> lines.</p>
<p>Frontmatter is a way to customise the metadata page directly within your Markdown files, and most importantly, frontmatter is used for SEO.</p>
<pre><code class="lang-markdown"><span class="hljs-section"># docs/getting-started.mdx</span>
---
title: Welcome to Awesome Project
<span class="hljs-section">description: Some awesome docs!
---</span>

<span class="hljs-section"># Welcome!</span>
</code></pre>
<p>Below is a list of some of the <a target="_blank" href="https://use.docs.page/frontmatter">important frontmatter properties</a> in docs.page, including their type and default values:</p>
<ul>
<li><p><code>title</code> (string): The page’s title used in metadata, social cards, and displayed as the main heading.</p>
</li>
<li><p><code>description</code> (string): A summary of the page appears in metadata for SEO and link previews.</p>
</li>
<li><p><code>image</code> (string): URL of an asset used in social cards and (if enabled) shown at the top of the page.</p>
</li>
<li><p><code>redirect</code> (string): A URL to forward visitors to. When set, the page’s content is bypassed.</p>
</li>
<li><p><code>showPageTitle</code> (boolean): Toggle whether the page title appears as a heading at the top.</p>
</li>
<li><p><code>showPageImage</code> (boolean): Toggle whether the front-matter image is rendered at the top.</p>
</li>
<li><p><code>noindex</code> (boolean): If true, instructs search engines not to index the page.</p>
</li>
</ul>
<p><a target="_blank" href="https://use.docs.page/frontmatter">Refer to the documentation</a> for more detail and other frontmatter property information.</p>
<h2 id="heading-how-to-add-assets-to-your-docs">How to Add Assets to Your Docs</h2>
<p>You can include assets, such as images and videos, in your documentation. You can add both remote and local assets.</p>
<h3 id="heading-remote-assets">Remote Assets</h3>
<p>To add remote assets to your documentation, you can reference them directly in your markdown files.</p>
<p>For example, to include an image from a URL:</p>
<pre><code class="lang-markdown"><span class="hljs-section"># getting-started.mdx</span>
---
title: Welcome to get started
<span class="hljs-section">description: Some awesome docs!
---</span>

<span class="hljs-section"># Welcome!</span>

![<span class="hljs-string">Natural</span>](<span class="hljs-link">https://cdn.pixabay.com/photo/2023/04/19/19/11/lake-7938396_960_720.jpg</span>)
</code></pre>
<h3 id="heading-local-assets">Local Assets</h3>
<p>To use local assets in your documentation, create an <code>assets</code> folder inside the <code>docs/</code> directory. Then, add images and videos to the assets folder and reference them in your Markdown files.</p>
<p>Check out the following to better understand:</p>
<pre><code class="lang-bash">docs/
  assets/
    natural.png
  index.mdx
</code></pre>
<p>Within your markdown file, you can reference the image using a relative path:</p>
<pre><code class="lang-markdown">![<span class="hljs-string">Description</span>](<span class="hljs-link">/assets/natural.png</span>)
</code></pre>
<h3 id="heading-different-between-local-vs-remote-assets">Different between Local vs Remote Assets</h3>
<p>Local assets (PNG, JPG, PDF, and so on) are files stored within your project's public folder, while remote assets are files hosted on an external server. You can access your local assets using your domain URL.</p>
<pre><code class="lang-markdown">![<span class="hljs-string">Natural</span>](<span class="hljs-link">./assets/logo.png</span>)
</code></pre>
<p>On the other hand, remote assets are stored on a different server (image hosting), as I mentioned. You can access remote assets with a full URL.</p>
<p>The best examples of remote assets include images from Unsplash, Pixabay, and Pexels that can be used directly in your MDX file.</p>
<pre><code class="lang-markdown">![<span class="hljs-string">Natural</span>](<span class="hljs-link">https://images.unsplash.com/photo-1728044849236-5e8a061e1895</span>)
</code></pre>
<p>You can use remote and local assets based on your requirements – both have advantages and disadvantages. With remote assets, you can add an image directly in your mdx file. When using local assets, you add an image to the public folder and then reference it in your mdx file.</p>
<h2 id="heading-how-to-publish-your-documentation-website">How to Publish Your Documentation Website</h2>
<p>With docs.page, you can easily publish your documentation website. No configuration is required – once your documentation website is ready, you can just push your local code to a GitHub repository.</p>
<p>You can now access your documentation website immediately via the docs.page domain.</p>
<p>For example, if your GitHub repository is officialrajdeepsingh/docs-page-demo, your documentation will be available at <a target="_blank" href="https://docs.page/officialrajdeepsingh/docs-page-demo">https://docs.page/officialrajdeepsingh/docs-page-demo</a>.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1745849817613/c1b7b095-121d-4b64-bd7b-a834ca87f8b5.png" alt="publish your documentation website" class="image--center mx-auto" width="1920" height="1048" loading="lazy"></p>
<h2 id="heading-how-to-live-preview-upcoming-changes-to-your-docs-website">How to Live Preview Upcoming Changes to Your Docs Website</h2>
<p>You can view previews of upcoming changes to your documentation before going public. As your documentation website grows, use the <a target="_blank" href="https://github.com/apps/docs-page">docs.page Github app</a> – any pull request you create in your Github repository automatically generates a unique live preview URL.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1746278906018/0a299083-ff0a-4aea-a94e-95f94741e9af.png" alt="0a299083-ff0a-4aea-a94e-95f94741e9af" class="image--center mx-auto" width="1920" height="961" loading="lazy"></p>
<p>To configure the docs page of the GitHub application in your repository, follow these steps:</p>
<ol>
<li><p>Go to <a target="_blank" href="https://github.com/apps/docs-page">https://github.com/apps/docs-page</a></p>
</li>
<li><p>Click on the install button.</p>
</li>
<li><p>Select the GitHub account</p>
</li>
<li><p>Select All and single repository.</p>
</li>
<li><p>Click on the install button</p>
</li>
<li><p>Next, enter the password and OTP.</p>
</li>
<li><p>Now if your application is successful, install it in your repository.</p>
</li>
</ol>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1746280111575/06d449cd-917a-4908-8d5e-db90cffd3c0f.gif" alt="Creates live previews in your github repository" class="image--center mx-auto" width="800" height="401" loading="lazy"></p>
<p>Whenever you or another developer create a pull request in your repository, the docs page application creates live previews for you.</p>
<h2 id="heading-conclusion"><strong>Conclusion</strong></h2>
<p>docs.page is a free, open-source project that allows you to create instant, fast, and beautiful documentation without requiring any configuration.</p>
<p>I think docs.page offers the best solution for documentation. You can easily set up and deploy your documentation website with the help of docs.page cloud service.</p>
<p>For now, it’s completely free to deploy a documentation website with a <a target="_blank" href="http://docs.page">docs.page</a>, and I hope it stays that way.</p>
<p>If <a target="_blank" href="http://docs.page">docs.page</a> ever decides to charge for their services, that could be troublesome. Hopefully, in that case, they’ll provide a clear guide on how to deploy your website on another cloud platform.</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Use Lazygit to Improve Your Git Workflow ]]>
                </title>
                <description>
                    <![CDATA[ Lazygit is an open-source command line terminal UI for Git commands that I’ve used for the last couple of years, and it’s become my new best friend. Basically, the Lazygit tool is a wrapper for the Git command line that replaces it with a UI. Instead... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-use-lazygit-to-improve-your-git-workflow/</link>
                <guid isPermaLink="false">67f7cc7aaf72aa46d378eaa0</guid>
                
                    <category>
                        <![CDATA[ Lazygit-tutorial ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Lazygit ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Git ]]>
                    </category>
                
                    <category>
                        <![CDATA[ version control ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Rajdeep Singh ]]>
                </dc:creator>
                <pubDate>Thu, 10 Apr 2025 13:49:46 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/res/hashnode/image/upload/v1744293114488/5332db88-bff6-4aef-91eb-3423f3b95e1a.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p><a target="_blank" href="https://github.com/jesseduffield/lazygit">Lazygit</a> is an open-source command line terminal UI for Git commands that I’ve used for the last couple of years, and it’s become my new best friend.</p>
<p>Basically, the Lazygit tool is a wrapper for the Git command line that replaces it with a UI. Instead of typing out Git commands again and again in your terminal, you can use keyboard shortcuts to commit, push, pull, create, edit, and delete branches in your project.</p>
<p>In simple terms, Lazygit helps you increase your productivity while working with Git.</p>
<p>In this article, we'll walk through the essential features of Lazygit, and I’ll show you how it works.</p>
<h2 id="heading-table-of-contents">Table of Contents:</h2>
<ol>
<li><p><a class="post-section-overview" href="#heading-how-to-install-lazygit">How to Install Lazygit</a></p>
</li>
<li><p><a class="post-section-overview" href="#how-to-use-lazygit">How to Use Lazygit</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-shortcuts-and-key-mappings-in-lazygit">Shortcuts and Key Mappings in Lazygit</a></p>
</li>
<li><p><a class="post-section-overview" href="#other-keybindings-in-lazygit">Other Keybindings in Lazygit</a></p>
</li>
<li><p><a class="post-section-overview" href="#conclusion">Conclusion</a></p>
</li>
</ol>
<h2 id="heading-how-to-install-lazygit">How to Install Lazygit</h2>
<p>Before we start, you’ll need to make sure it’s installed on your machine. You can install the tool in your system using the following methods (depending on your system):</p>
<h3 id="heading-homebrew">Homebrew</h3>
<p>You can <a target="_blank" href="https://formulae.brew.sh/formula/lazygit#default">install lazygit</a> in macOS using Homebrew like this:</p>
<pre><code class="lang-bash">brew install lazygit
</code></pre>
<h3 id="heading-scoop-windows">Scoop (Windows)</h3>
<p>You can <a target="_blank" href="https://scoop.sh/#/apps?q=lazygit">install lazygit</a> in Windows using Scoop like this:</p>
<pre><code class="lang-bash"><span class="hljs-comment"># Add the extras bucket</span>
scoop bucket add extras

<span class="hljs-comment"># Install lazygit</span>
scoop install lazygit
</code></pre>
<h3 id="heading-arch-linux">Arch Linux</h3>
<p>You can <a target="_blank" href="https://aur.archlinux.org/packages/lazygit-git">install lazygit</a> in Arch using Pacman like this:</p>
<pre><code class="lang-bash">sudo pacman -S lazygit
</code></pre>
<h3 id="heading-ubuntu-and-debian">Ubuntu and Debian</h3>
<p>You can install lazygit in Ubuntu and Debian using the following command:</p>
<pre><code class="lang-bash">LAZYGIT_VERSION=$(curl -s <span class="hljs-string">"https://api.github.com/repos/jesseduffield/lazygit/releases/latest"</span> | \grep -Po <span class="hljs-string">'"tag_name": *"v\K[^"]*'</span>)
curl -Lo lazygit.tar.gz <span class="hljs-string">"https://github.com/jesseduffield/lazygit/releases/download/v<span class="hljs-variable">${LAZYGIT_VERSION}</span>/lazygit_<span class="hljs-variable">${LAZYGIT_VERSION}</span>_Linux_x86_64.tar.gz"</span>
tar xf lazygit.tar.gz lazygit
sudo install lazygit -D -t /usr/<span class="hljs-built_in">local</span>/bin/
</code></pre>
<p>Verify the correct installation of lazygit:</p>
<pre><code class="lang-bash">lazygit --version
</code></pre>
<p>The command output looks like this:</p>
<pre><code class="lang-bash">➜  lazygit --version
commit=, build date=, build <span class="hljs-built_in">source</span>=nix, version=0.44.1, os=linux, arch=amd64, git version=2.47.0
</code></pre>
<h3 id="heading-fedora-and-rhel">Fedora and RHEL</h3>
<p>You can install lazygit in Fedora and RHEL using DNF like this:</p>
<pre><code class="lang-bash">sudo dnf copr <span class="hljs-built_in">enable</span> atim/lazygit -y
sudo dnf install lazygit
</code></pre>
<h3 id="heading-nixos">NixOS</h3>
<p>You can <a target="_blank" href="https://search.nixos.org/packages?channel=24.11&amp;from=0&amp;size=50&amp;sort=relevance&amp;type=packages&amp;query=lazygit">install lazygit</a> in NixOS using the following method:</p>
<pre><code class="lang-bash"><span class="hljs-comment"># with nix-shell</span>
nix-shell -p lazygit

<span class="hljs-comment"># with nix-env</span>
nix-env -iA lazygit

<span class="hljs-comment"># with /etc/nixos/configuration.nix</span>
environment.systemPackages = [
  pkgs.lazygit
];
<span class="hljs-comment"># or with enable lazygit flakes</span>
nix run nixpkgs<span class="hljs-comment">#lazygit</span>
</code></pre>
<h2 id="heading-how-to-use-lazygit">How to Use Lazygit</h2>
<p>To use Lazygit, you don’t need any advanced knowledge about Lazygit or the Git CLI. If you are a beginner, that’s okay – I’ll walk you through the process and the basics here.</p>
<p>The main thing to understand is how the key mappings (shortcut keys) work. In this tutorial, I won’t discuss every key mapping, but I’ll teach you about some of the most common Lazygit key mappings which you’ll use on a daily basis. They’ll help you build a solid base for using the tool effectively.</p>
<p>To use Lazygit, first open the terminal you use. For example, I’m using the GNOME distro, so I’ll use the <a target="_blank" href="https://gitlab.gnome.org/chergert/ptyxis">Ptyxis terminal</a>.</p>
<p>Type the <code>lazygit</code> command in your terminal:</p>
<pre><code class="lang-bash">lazygit
</code></pre>
<p>The command output should look like this in your terminal:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1743685042853/ab3f10f0-0d13-44d3-a86a-a58676cf30a5.gif" alt="Lazygit cli demo" class="image--center mx-auto" width="1000" height="545" loading="lazy"></p>
<p>The Lazygit UI is divided into six panels, or sections. Each panel serves a specific use case. Let’s explore these panels in more detail. You can see them highlighted in the image below:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1743687006438/5ca2451e-d4a0-42a7-89b2-0b94fd4ca162.png" alt="Explore the Lazygit panels" class="image--center mx-auto" width="1920" height="1048" loading="lazy"></p>
<h3 id="heading-panels-or-sections-in-lazygit">Panels or Sections in Lazygit</h3>
<p>As I mentioned above, there are six main panels in Lazygit. They are:</p>
<ol>
<li><p>Status</p>
</li>
<li><p>Files</p>
</li>
<li><p>Branches</p>
</li>
<li><p>Commits</p>
</li>
<li><p>Stash</p>
</li>
<li><p>Previews</p>
</li>
</ol>
<p>The most important panels in Lazygit are files, branches, and commits, but we’ll examine each of the six now.</p>
<h4 id="heading-status-panel">Status panel</h4>
<p>The status panel provides an overview of your current repository and the current checked-out branch, including local and remote changes.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1743759630157/a7ef738b-5353-4941-9eb5-073d3235aaba.png" alt="Status panel in Lazygit" class="image--center mx-auto" width="648" height="98" loading="lazy"></p>
<p>Also, when you click on the status panel text, it opens a new tab or panel where it shows the recently opened repository list.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1743760171736/8edb2f41-86ad-4e64-95f2-b1310d8c6f57.png" alt="Recently opened repos" class="image--center mx-auto" width="1920" height="1048" loading="lazy"></p>
<h4 id="heading-files-panel">Files panel</h4>
<p>The Files panel shows lists of the files in your repository that have been modified or changed. You can see files that you’ve deleted or discarded and unstaged.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1743760570130/c891940b-4ba2-4fcb-a867-817b74e53618.png" alt="Files panel in Lazygit" class="image--center mx-auto" width="652" height="305" loading="lazy"></p>
<h4 id="heading-branches-panel">Branches panel</h4>
<p>The Branches panel shows lists of local and remote branches which are available in this repository.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1743761276628/b34dc945-11c8-482d-8c4d-51783c68bf55.png" alt="Branches panel in lazygit" class="image--center mx-auto" width="649" height="256" loading="lazy"></p>
<h4 id="heading-commits-panel">Commits panel</h4>
<p>The Commits panel shows a list of commits in the current branch, which allows you to view, checkout, or interact with (view/undo/and so on) specific commits.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1743761671236/80a7321a-8d16-4add-bc4a-db52d2987836.png" alt="commits panel in lazygit" class="image--center mx-auto" width="649" height="300" loading="lazy"></p>
<h4 id="heading-stashes-panel">Stashes panel</h4>
<p>The Stashes panel helps you manage your stashed changes, allowing you to apply, drop, or view them. Git stash is a location for storing uncommitted changes (modified, staged, or untracked files) in a hidden place, letting you switch branches without committing or discarding them.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1743764679570/d7233d92-cfb0-4757-a338-bcc3d9fec2b8.png" alt="Stashes panel in laygit" class="image--center mx-auto" width="650" height="220" loading="lazy"></p>
<h4 id="heading-preview-panel">Preview panel</h4>
<p>The preview panel lets you preview unstaged changes, commits, logs, file content, and so on in Lazygit.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1743765844602/7c492361-bbcf-4d2c-8588-b0c3e8704132.png" alt="Preview panel in lazygit" class="image--center mx-auto" width="1920" height="1048" loading="lazy"></p>
<p>To switch between panels, use the left and right arrow keys or the specific keybindings displayed at the top of each panel.</p>
<p>Press <code>1</code> to open the Status panel, <code>2</code> for Files, <code>3</code> for Branches, <code>4</code> for Commits, and <code>5</code> for the Stash panel.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1743766590099/91689f4d-eba3-47cf-80a6-e18307c326cd.gif" alt="Navigation between panels in lazygit" class="image--center mx-auto" width="1000" height="545" loading="lazy"></p>
<h2 id="heading-shortcuts-and-key-mappings-in-lazygit"><strong>Shortcuts and Key Mappings in Lazygit</strong></h2>
<p>Lazygit is especially popular because of its shortcuts. You don’t need to write the same Git commands in the terminal over and over. Rather, you just need to use a shortcut.</p>
<p>For example, usually when you commit a file, you’ll first add the file using <code>git add</code> and then commit the file using <code>git commit</code>.</p>
<p>But in Lazygit, you just have to select the file using your mouse or the up and down keys and press space to commit the file.</p>
<p>In Lazygit, everything works around the shortcut commands, and you use shortcuts to perform common Git operations. Here are a few essential commands we’ll go over in this tutorial:</p>
<ul>
<li><p><code>a</code> – Stage or unstage all the files available in the Files panel.</p>
</li>
<li><p><code>space</code> (file panel) – Stage or unstage a single file in the Files panel.</p>
</li>
<li><p><code>c</code> – Commit staged changes by opening a commit message editor.</p>
</li>
<li><p><code>p</code> – Push commits to the remote repository.</p>
</li>
<li><p><code>P</code> – Pull changes from the remote repository.</p>
</li>
<li><p><code>z</code> – Undo the commit.</p>
</li>
<li><p><code>s</code> – Stash changes, allowing you to switch branches or perform other operations.</p>
</li>
<li><p><code>S</code> – View and apply stashed changes.</p>
</li>
<li><p><code>n</code> – Create a new branch.</p>
</li>
<li><p><code>d</code> – Delete your branch.</p>
</li>
<li><p><code>y</code> – Copy to clipboard.</p>
</li>
<li><p><code>M</code> – Merge branch.</p>
</li>
<li><p><code>space</code> (branches panel) – Check out the selected target branch.</p>
</li>
<li><p><code>e</code> – Edit or open the file in an external editor.</p>
</li>
<li><p><code>q</code> – Quit Lazygit and return to the terminal.</p>
</li>
<li><p><code>d</code> – Discard any changes in the file.</p>
</li>
<li><p><code>?</code> – Open the keybinding menu.</p>
</li>
</ul>
<p>Now let’s go over these shortcuts so you can understand how they work and see them in action.</p>
<h3 id="heading-how-to-commit-a-file">How to Commit a File</h3>
<p>To commit a file in Lazygit, first select the file you need by pressing the <code>space</code> key or the <code>a</code> key or double-clicking on the file. Then press <code>c</code>, and a new panel should open. There, you can add a message and hit enter to commit the file.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1743770682782/cbd83578-a286-482f-aeaa-31a9715a5483.gif" alt="cbd83578-a286-482f-aeaa-31a9715a5483" class="image--center mx-auto" width="1000" height="545" loading="lazy"></p>
<h3 id="heading-how-to-pull-and-push-code">How to Pull and Push Code</h3>
<p>To pull remote code from the Git server (Github, GitLab, Gitea, and so on), you can press <code>p</code> (lower case p):</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1743774642242/decec44c-7622-432a-9da5-81b14b60ef8a.gif" alt="decec44c-7622-432a-9da5-81b14b60ef8a" class="image--center mx-auto" width="680" height="371" loading="lazy"></p>
<p>To push local code into a Git server, you can press <code>P</code> (upper case P):</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1743842516002/37647a76-afe5-4d4b-acfc-fc85f1010749.gif" alt="37647a76-afe5-4d4b-acfc-fc85f1010749" class="image--center mx-auto" width="1000" height="545" loading="lazy"></p>
<h3 id="heading-how-to-create-and-delete-a-branch">How to Create and Delete a Branch</h3>
<p>To create a new branch in Lazygit, press <code>n</code>. A new panel will open where you’ll add the name of the branch and hit Enter.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1743843881624/6c4db14e-0102-4333-be56-5d3796ab1c50.gif" alt="Create a new branch in lazygit" class="image--center mx-auto" width="1000" height="545" loading="lazy"></p>
<p>To delete a branch, press <code>d</code> and then specify whether you want to delete the branch in a local or remote repository. In the following example, I’m deleting a local branch.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1743847541934/34e378b6-03ac-4e6d-93d0-35aaeda39e57.gif" alt="34e378b6-03ac-4e6d-93d0-35aaeda39e57" class="image--center mx-auto" width="1000" height="545" loading="lazy"></p>
<blockquote>
<p>Note: To delete and create a new branch in Lazygit, first select the branch panel and then press the corresponding shortcut key for deleting a branch. Press the d key to delete, and then to create a branch press the n key. Otherwise, it won’t work.</p>
</blockquote>
<h3 id="heading-how-to-undo-a-commit">How to Undo a Commit</h3>
<p>To undo the last commit in Lazygit, just press <code>z</code>. A new panel will open, showing the details of the commit you are undoing. Then, hit Enter.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1743852917448/a3f2cab7-2806-48e4-a749-90f821b537dc.gif" alt="Undo commit in lazygit" class="image--center mx-auto" width="1000" height="545" loading="lazy"></p>
<h3 id="heading-how-to-merge-a-branch">How to Merge a Branch</h3>
<p>To merge a branch, press <code>M</code> (capital M). To open the merge options, choose the merge type, then hit Enter.</p>
<h4 id="heading-merge-type">Merge type:</h4>
<ul>
<li><p><strong>Merge:</strong> A standard merge, preserving the branch history.</p>
</li>
<li><p><strong>Squash merge:</strong> Combines all commits from the branch into a single commit on the target branch.</p>
</li>
<li><p><strong>Squash merge and leave uncommitted</strong>: Same as squash merge, but leaves the changes uncommitted.</p>
</li>
</ul>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1743921595283/e46e0c89-b69a-4462-acd3-295045c99dfd.gif" alt="Merge branch in lazygit" class="image--center mx-auto" width="1000" height="545" loading="lazy"></p>
<h3 id="heading-how-to-resolve-merge-conflicts">How to Resolve Merge Conflicts</h3>
<p>To resolve merge conflicts in Lazygit, first merge a branch by pressing <code>M</code>, then choose the merge type (which I describe in the subsection on how to merge a branch) and hit Enter.</p>
<p>If any merge conflicts occur, the conflicting file(s) appear in the files panel. Press Enter to view the merge conflicts in the preview panel and navigate between conflicts using the up and down keys. Select the correct merge conflicts, press the space key, and your merge issue will be resolved.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1743921640247/e5b7f971-f027-47df-be4c-a90b356e24f8.gif" alt="resolve merge conflicts in lazygit" class="image--center mx-auto" width="1000" height="545" loading="lazy"></p>
<h3 id="heading-how-to-discard-changes">How to Discard Changes</h3>
<p>To discard or drop any changes in a file or commit, press <code>d</code>.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1743774406564/bc5b91fb-2d33-41d0-95b9-667478c4c8db.gif" alt="bc5b91fb-2d33-41d0-95b9-667478c4c8db" class="image--center mx-auto" width="1000" height="545" loading="lazy"></p>
<h3 id="heading-how-to-copy">How to Copy</h3>
<p>To copy a file name, path, commit hash, message, URL, author, or any other details, first select the commit or file, then press <code>y</code> to copy the information.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1743856793802/e23d9e5c-b0b4-40a0-8124-f94669b377c0.gif" alt="e23d9e5c-b0b4-40a0-8124-f94669b377c0" class="image--center mx-auto" width="1000" height="545" loading="lazy"></p>
<h2 id="heading-other-keybindings-in-lazygit">Other Keybindings in Lazygit</h2>
<p>There are other keybindings in Lazygit which I did not discuss in this article. To learn about every keybinding, you can check out the keybindings menu. Open the keybindings menu and press the <code>?</code>.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1743843262905/a4aba097-999b-4ff8-bd00-661181d96aad.gif" alt="a4aba097-999b-4ff8-bd00-661181d96aad" class="image--center mx-auto" width="1000" height="545" loading="lazy"></p>
<p>When you open the keybindings help menu, it changes according to the panel you’re in.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1743915037200/9339b7b1-b2a4-45e5-8a51-5be0a9f2a319.gif" alt="9339b7b1-b2a4-45e5-8a51-5be0a9f2a319" class="image--center mx-auto" width="1000" height="545" loading="lazy"></p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>Lazygit helps you become more productive when working with Git or Git commands. As a beginner, starting with Lazygit can be somewhat challenging because of its key mappings, but once you get the hang of them, they’re pretty easy to remember and use.</p>
<p>If you are a first-time Lazygit user, my suggestion is to avoid using Lazygit on a working repository. Instead, create a demo repository and try it out/practice.</p>
<p>To learn more about <a target="_blank" href="https://github.com/jesseduffield/lazygit/blob/master/docs/keybindings/Keybindings_en.md">LazyGit keybindings or shortcuts</a>, you can refer to the Lazygit documentation. You can also check out the following YouTube tutorials for beginners:</p>
<ul>
<li><p><a target="_blank" href="https://www.youtube.com/watch?v=A6F_8ajlrYQ">LazyGIt - A Faster, Easier Way to Use Git on Terminal &amp; NeoVim</a></p>
</li>
<li><p><a target="_blank" href="https://www.youtube.com/watch?v=Ihg37znaiBo">Lazygit - The Best Way To Use Git On The Terminal &amp; Neovim</a></p>
</li>
<li><p><a target="_blank" href="https://www.youtube.com/watch?v=06lEP59XAgM">My new favorite way to use Git</a></p>
</li>
<li><p><a target="_blank" href="https://www.youtube.com/watch?v=dSWJKcEiAaM">LazyGit: Effortless Git in Your Terminal!</a></p>
</li>
</ul>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ Learn to Use GitHub Actions: a Step-by-Step Guide ]]>
                </title>
                <description>
                    <![CDATA[ GitHub Actions are one of the most helpful features of GitHub. Actions help you automate, build, test, and deploy your app from your GitHub. They also help you perform code reviews and tests, manage branches, triage issues, and more. In simple terms,... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/learn-to-use-github-actions-step-by-step-guide/</link>
                <guid isPermaLink="false">6789193ad7f606e4fbf4d0a0</guid>
                
                    <category>
                        <![CDATA[ GitHub ]]>
                    </category>
                
                    <category>
                        <![CDATA[ GitHub Actions ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Rajdeep Singh ]]>
                </dc:creator>
                <pubDate>Thu, 16 Jan 2025 14:35:38 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/res/hashnode/image/upload/v1736973439529/e0445f1c-62df-441f-a335-96c468a373da.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p><a target="_blank" href="https://docs.github.com/en/actions">GitHub Actions</a> are one of the most helpful features of GitHub. Actions help you automate, build, test, and deploy your app from your GitHub. They also help you perform code reviews and tests, manage branches, triage issues, and more.</p>
<p>In simple terms, the GitHub workflow creates an environment (virtual machine-based on the <strong>runner</strong>) to test, build, and deploy your code into the cloud based on the action that you describe in the GitHub Action file.</p>
<p>This tutorial teaches you how to add a GitHub Action, providing an example and step-by-step guidance. It is suitable for both beginners and intermediate developers.</p>
<h2 id="heading-table-of-contents">Table of Contents:</h2>
<ol>
<li><p><a target="_blank" href="https://preview.freecodecamp.org/66dac163d514d27826eb3bef#heading-prerequisites">Prerequisites</a></p>
</li>
<li><p><a target="_blank" href="https://preview.freecodecamp.org/66dac163d514d27826eb3bef#heading-key-github-actions-concepts">Key GitHub Actions Concepts</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-create-a-github-action-in-your-repository">How to Create a GitHub Action in Your Repository</a></p>
<ul>
<li><p><a class="post-section-overview" href="#heading-create-a-github-action-using-the-github-ui">Create a GitHub Action Using the GitHub UI</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-create-a-github-action-locally-with-your-ide">Create a GitHub Action Locally with Your IDE</a></p>
</li>
</ul>
</li>
<li><p><a class="post-section-overview" href="#heading-github-actions-syntax">GitHub Actions Syntax</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-github-actions-examples">GitHub Actions Examples</a></p>
</li>
<li><p><a target="_blank" href="https://preview.freecodecamp.org/66dac163d514d27826eb3bef#heading-conclusion">Conclusion</a></p>
</li>
</ol>
<h2 id="heading-prerequisites">Prerequisites</h2>
<p>To get the most out of this article, you should have at least a basic knowledge of how to use GitHub and YAML. If you don't know GitHub fundamentals, check out <a target="_blank" href="https://www.freecodecamp.org/news/guide-to-git-github-for-beginners-and-experienced-devs/">this in-depth tutorial on Git and GitHub</a>. And <a target="_blank" href="https://www.freecodecamp.org/news/what-is-yaml-the-yml-file-format/">here’s an introduction to YAML</a>.</p>
<p>You’ll also need to understand the main concepts behind <strong>events</strong>, <strong>workflows</strong>, <strong>jobs</strong>, and <strong>runners</strong> and why they’re important when creating a GitHub Action.</p>
<p>These are the key ingredients of GitHub actions, so we’ll go through them one by one before diving into the primary part of the tutorial.</p>
<h2 id="heading-key-github-actions-concepts">Key GitHub Actions Concepts</h2>
<h3 id="heading-workflows"><strong>Workflows</strong></h3>
<p>A workflow is a configurable automated process that runs one or more jobs. It is created with a YAML file in your repository and runs when an event triggers it. Workflows can also be triggered manually or on a defined schedule.</p>
<p>Workflows are defined in the <code>.github/workflows</code> directory in a repository. In the repository, you can create multiple workflows that perform different tasks, such as:</p>
<ol>
<li><p>Building and testing pull requests</p>
</li>
<li><p>Deploying your application on the cloud</p>
</li>
<li><p>Running a test on every pull request</p>
</li>
</ol>
<h3 id="heading-events"><strong>Events</strong></h3>
<p>An event is a specific activity in a repository that triggers or runs a workflow in your GitHub repository. For example, when you push code to the repository, it triggers the <code>push</code> event. The same happens when you create a new issue – it triggers the <code>issues</code> event. And when somebody makes a pull request in your repository, it triggers the <code>pull_request</code> event.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1736342712858/866f61a7-4750-45bf-82ea-d4e9535069a4.png" alt="Describing the different event types in GitHub" class="image--center mx-auto" width="993" height="479" loading="lazy"></p>
<p>These are some common GitHub Action events:</p>
<ol>
<li><p>Push</p>
</li>
<li><p>pull_request</p>
</li>
<li><p>release</p>
</li>
<li><p>label</p>
</li>
<li><p>issues</p>
</li>
<li><p>milestone</p>
</li>
<li><p>label</p>
</li>
</ol>
<p>The <code>push</code>, <code>release</code>, and <code>pull_request</code> events are the most common events. To read more about events, you can <a target="_blank" href="https://docs.github.com/en/actions/writing-workflows/choosing-when-your-workflow-runs/events-that-trigger-workflows#about-events-that-trigger-workflows">check out the GitHub documentation</a>.</p>
<p>It’s a good idea to specify the event type in a GitHub Action. For example, specifying the <code>pull_request</code> event will trigger the action whenever any user creates a pull request in the GitHub repository.</p>
<pre><code class="lang-yaml"><span class="hljs-comment"># .github/workflows/demo.yml</span>

<span class="hljs-attr">on:</span>
  <span class="hljs-attr">issues:</span>
    <span class="hljs-attr">types:</span> [<span class="hljs-string">opened</span>, <span class="hljs-string">edited</span>, <span class="hljs-string">milestoned</span>]

  <span class="hljs-attr">pull_request:</span>
    <span class="hljs-attr">types:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">opened</span>
    <span class="hljs-attr">branches:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">'releases/**'</span>
</code></pre>
<p>This is helpful because, if you don’t declare a specific event activity type in your event type, it can lead to unnecessary resources getting used. The GitHub Action will be triggered with every new pull request – so it’s best to define which type of event you’re using.</p>
<h3 id="heading-jobs">Jobs</h3>
<p>GitHub Action jobs run in parallel by default. A GitHub Action workflow runs one or more jobs, each containing a set of steps that execute commands or actions. Here’s an example:</p>
<pre><code class="lang-yaml"><span class="hljs-comment"># .github/workflows/demo.yml</span>

<span class="hljs-attr">name:</span> <span class="hljs-string">Demo</span> <span class="hljs-string">Workflows</span>

<span class="hljs-attr">on:</span>
   <span class="hljs-attr">push:</span>

<span class="hljs-comment"># A workflow run is made up of one or more jobs that can run sequentially or in parallel</span>
<span class="hljs-attr">jobs:</span>

<span class="hljs-attr">jobs:</span>
</code></pre>
<p>You can set one job to depend on (an)other job(s). If jobs don’t have dependencies, they’ll run in parallel. When one job depends on another, it will wait for that job to finish before it starts.</p>
<pre><code class="lang-yaml"><span class="hljs-comment"># .github/workflows/demo.yml</span>

<span class="hljs-attr">jobs:</span>
  <span class="hljs-attr">build:</span>
    <span class="hljs-attr">name:</span> <span class="hljs-string">Build</span>
    <span class="hljs-attr">needs:</span> [ <span class="hljs-string">Development</span> ]
    <span class="hljs-attr">steps:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Build</span> <span class="hljs-string">and</span> <span class="hljs-string">deploy</span> <span class="hljs-string">on</span> <span class="hljs-string">Cloud</span>
  <span class="hljs-attr">dev:</span>
    <span class="hljs-attr">name:</span> <span class="hljs-string">Development</span>
    <span class="hljs-attr">steps:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Run</span> <span class="hljs-string">the</span> <span class="hljs-string">developer</span>

  <span class="hljs-attr">Test:</span>
    <span class="hljs-attr">needs:</span> [ <span class="hljs-string">build</span>, <span class="hljs-string">dev</span> ]
    <span class="hljs-attr">name:</span> <span class="hljs-string">Testing</span>
    <span class="hljs-attr">steps:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Testing</span> <span class="hljs-string">the</span> <span class="hljs-string">application</span>
</code></pre>
<h3 id="heading-runners">Runners</h3>
<p><strong>Runners</strong> are servers that execute workflows when triggered. Each runner can handle only one job at a time. GitHub offers runners for Ubuntu Linux, Microsoft Windows, and MacOS to run your workflows.</p>
<pre><code class="lang-yaml"><span class="hljs-comment"># .github/workflows/demo.yml</span>

<span class="hljs-attr">name:</span> <span class="hljs-string">Demo</span> <span class="hljs-string">workflows</span>

<span class="hljs-attr">on:</span>
  <span class="hljs-comment"># Triggers the workflow on push or pull request events but only for the "main" branch</span>
   <span class="hljs-attr">push:</span>
    <span class="hljs-attr">branches:</span> [ <span class="hljs-string">"main"</span> ]

<span class="hljs-comment"># A workflow run is made up of one or more jobs that can run sequentially or in parallel</span>
<span class="hljs-attr">jobs:</span>
  <span class="hljs-comment"># This workflow contains a single job called "build"</span>
  <span class="hljs-attr">build:</span>
    <span class="hljs-comment"># The type of runner that the job will run on</span>
    <span class="hljs-attr">runs-on:</span> <span class="hljs-string">ubuntu-latest</span>
</code></pre>
<p>To define the runners, specify the runner value in the <code>runs-on</code> option. You can provide it as a <strong>single string</strong> or an <strong>array of strings</strong>.</p>
<pre><code class="lang-yaml"><span class="hljs-comment"># .github/workflows/demo.yml</span>

<span class="hljs-comment"># String</span>
<span class="hljs-attr">runs-on:</span> <span class="hljs-string">ubuntu-latest</span>
<span class="hljs-comment"># Array of string</span>
<span class="hljs-attr">runs-on:</span> [ <span class="hljs-string">ubuntu-latest</span>, <span class="hljs-string">windows-latest</span>, <span class="hljs-string">macos-latest</span> ]
</code></pre>
<p>Now that you’re familiar with the key elements of GitHub Actions and how they work, let’s see how to use Actions in practice.</p>
<h2 id="heading-how-to-create-a-github-action-in-your-repository">How to Create a GitHub Action in Your Repository</h2>
<p>You can create a GitHub Action in GitHub very easily. There are two ways to do it:</p>
<ol>
<li><p>Using the Github UI</p>
</li>
<li><p>Locally with your IDE</p>
</li>
</ol>
<p>Many developers use the GitHub UI to create an Action. This is a common way to create an Action. You don't need to create a <code>.github/workflow</code> folder when you use the GitHub UI. GitHub automatically creates this folder for you. On the other hand, for complex Github actions, you'll typically use your IDE.</p>
<p>Let’s look at each approach now.</p>
<h3 id="heading-create-a-github-action-using-the-github-ui">Create a GitHub Action Using the GitHub UI</h3>
<p>First, go to the GitHub repository where you want to create your GitHub Action.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1736510921442/e4b338f5-c928-4aba-953d-0df5adee0bd3.png" alt="GitHub repository where you want to create your action" class="image--center mx-auto" width="1920" height="961" loading="lazy"></p>
<p>To create the action, follow these steps:</p>
<h4 id="heading-1-click-on-the-action-tab">1. Click on the Action Tab</h4>
<p>Click on the Action tab to create a GitHub Action. You’ll see the following page:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1736511145120/79fc5a4a-26b9-4f41-bc7b-1c976ff47663.png" alt="Create the GitHub Action" class="image--center mx-auto" width="1920" height="961" loading="lazy"></p>
<h4 id="heading-2-select-the-workflow-action">2. Select the workflow action</h4>
<p>GitHub suggestions automatically work according to the nature of your project. Select the GitHub workflow and click on the configure button to create your action.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1736511580215/48ed1bb6-bc25-43fd-bf3d-2ac9f945f3eb.png" alt="Select the github workflow in github" class="image--center mx-auto" width="1920" height="961" loading="lazy"></p>
<h4 id="heading-3-create-the-github-workflow">3. Create the GitHub workflow</h4>
<p>You’ll see the following page where you can edit and create your action. Click on the commit change button to save the action.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1736858011259/267d62ad-f41b-449e-b0dd-dab3a9251ba1.png" alt="Edit and create your Action in Github." class="image--center mx-auto" width="1920" height="961" loading="lazy"></p>
<p>And that’s it – you’ve created your GitHub Action.</p>
<h3 id="heading-create-a-github-action-locally-with-your-ide">Create a GitHub Action Locally with Your IDE</h3>
<p>First, open your project in your current IDE, such as VS Code, Neovim, Vim, or whatever. Then, create a <code>.github/workflow/name-of-workflow.yml</code> file in your project. Copy and paste the following code and save and push your local code into the GitHub repository.</p>
<p>Following the GitHub workflow action example code is printed a hello world message.</p>
<pre><code class="lang-yaml"><span class="hljs-comment"># .github/workflows/demo.yml</span>

<span class="hljs-attr">name:</span> <span class="hljs-string">CI</span>

<span class="hljs-comment"># Controls when the workflow will run</span>
<span class="hljs-attr">on:</span>
  <span class="hljs-comment"># Triggers the workflow on push or pull request events but only for the "main" branch</span>
  <span class="hljs-attr">push:</span>
    <span class="hljs-attr">branches:</span> [ <span class="hljs-string">"main"</span> ]

<span class="hljs-comment"># A workflow run is made up of one or more jobs that can run sequentially or in parallel</span>
<span class="hljs-attr">jobs:</span>
  <span class="hljs-comment"># This workflow contains a single job called "build"</span>
  <span class="hljs-attr">build:</span>
    <span class="hljs-comment"># The type of runner that the job will run on</span>
    <span class="hljs-attr">runs-on:</span> <span class="hljs-string">ubuntu-latest</span>

    <span class="hljs-comment"># Steps represent a sequence of tasks that will be executed as part of the job</span>
    <span class="hljs-attr">steps:</span>
      <span class="hljs-comment"># Checks out your repository under $GITHUB_WORKSPACE, so your job can access it</span>
      <span class="hljs-bullet">-</span> <span class="hljs-attr">uses:</span> <span class="hljs-string">actions/checkout@v4</span>

      <span class="hljs-comment"># Runs a single command using the runners shell</span>
      <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Run</span> <span class="hljs-string">a</span> <span class="hljs-string">one-line</span> <span class="hljs-string">script</span>
        <span class="hljs-attr">run:</span> <span class="hljs-string">echo</span> <span class="hljs-string">Hello,</span> <span class="hljs-string">world!</span>
</code></pre>
<p>I’m using the Neovim IDE to create a <code>.github/workflow/demo.yml</code> file. It looks like this.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1736935606919/aa187277-118a-4990-b240-684ced2f8a55.png" alt="Create an action locally using your IDE." class="image--center mx-auto" width="1900" height="797" loading="lazy"></p>
<h2 id="heading-github-actions-syntax">GitHub Actions Syntax</h2>
<p>To create a GitHub Action, it’s important to understand the GitHub Action syntax. In this section, you’ll learn some of the most common syntax you’ll use to create your Actions.</p>
<p>We’ll work with this example Action and go through it part by part below:</p>
<pre><code class="lang-yaml"><span class="hljs-comment"># .github/workflows/demo.yml</span>

<span class="hljs-attr">name:</span> <span class="hljs-string">Github</span> <span class="hljs-string">Action</span> <span class="hljs-string">Template</span> 

<span class="hljs-attr">on:</span>

  <span class="hljs-attr">pull_request:</span>
    <span class="hljs-attr">branches:</span> [ <span class="hljs-string">"main"</span> ]

  <span class="hljs-attr">schedule:</span>
    <span class="hljs-bullet">-</span> <span class="hljs-attr">cron:</span>  <span class="hljs-string">'30 5,17 * * *'</span>

  <span class="hljs-attr">workflow_call:</span>
    <span class="hljs-attr">inputs:</span>
      <span class="hljs-attr">username:</span>
        <span class="hljs-attr">description:</span> <span class="hljs-string">'A username passed from the caller workflow'</span>
        <span class="hljs-attr">default:</span> <span class="hljs-string">'john-doe'</span>
        <span class="hljs-attr">required:</span> <span class="hljs-literal">false</span>
        <span class="hljs-attr">type:</span> <span class="hljs-string">string</span>

  <span class="hljs-attr">permissions:</span>
    <span class="hljs-attr">actions:</span> <span class="hljs-string">read|write|none</span>

  <span class="hljs-comment"># permissions : read|write|none</span>

<span class="hljs-comment"># A workflow run is made up of one or more jobs that can run sequentially or in parallel</span>
<span class="hljs-attr">jobs:</span>

  <span class="hljs-comment"># This workflow contains a single job called "build"</span>

  <span class="hljs-attr">build:</span>

    <span class="hljs-attr">runs-on:</span> <span class="hljs-string">ubuntu-latest</span>

    <span class="hljs-comment"># Steps represent a sequence of tasks that will be executed as part of the job</span>

    <span class="hljs-attr">steps:</span>

      <span class="hljs-comment"># Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it</span>
      <span class="hljs-bullet">-</span> <span class="hljs-attr">uses:</span> <span class="hljs-string">actions/checkout@v4</span>
        <span class="hljs-attr">if:</span> <span class="hljs-string">${{</span> <span class="hljs-string">github.event_name</span> <span class="hljs-string">==</span> <span class="hljs-string">'pull_request'</span> <span class="hljs-string">&amp;&amp;</span> <span class="hljs-string">github.event.action</span> <span class="hljs-string">==</span> <span class="hljs-string">'unassigned'</span> <span class="hljs-string">}}</span>
        <span class="hljs-attr">shell:</span> <span class="hljs-string">zsh</span>
        <span class="hljs-attr">name:</span> <span class="hljs-string">NPM</span> <span class="hljs-string">Install</span> <span class="hljs-string">Package</span>
        <span class="hljs-attr">run:</span> <span class="hljs-string">npm</span> <span class="hljs-string">install</span>
        <span class="hljs-attr">with:</span>
          <span class="hljs-attr">GITHUB_TOKEN:</span> <span class="hljs-string">${{</span> <span class="hljs-string">secrets.GITHUB_TOKEN</span> <span class="hljs-string">}}</span>
          <span class="hljs-attr">first_name:</span> <span class="hljs-string">Github</span>
          <span class="hljs-attr">last_name:</span> <span class="hljs-string">Action</span>
          <span class="hljs-attr">args:</span> <span class="hljs-string">The</span> <span class="hljs-string">${{</span> <span class="hljs-string">github.event_name</span> <span class="hljs-string">}}</span> <span class="hljs-string">event</span> <span class="hljs-string">triggered</span> <span class="hljs-string">this</span> <span class="hljs-string">step.</span>
          <span class="hljs-attr">entrypoint:</span> <span class="hljs-string">/bin/echo</span>
</code></pre>
<p>Now, let’s understand each option that you can see in this GitHub Action example workflow:</p>
<ol>
<li><p><code>name</code>: The name describes the workflow name.</p>
</li>
<li><p><code>pull_request</code>: The pull request is part of the event type. It means somebody added a pull request in your repository and the following workflow was run.</p>
</li>
<li><p><code>schedule</code>: With a schedule, you can define the time schedule in your workflows. You can schedule a workflow to run at certain tasks on specific UTC times or based on intervals after five minutes, and so on.</p>
</li>
<li><p><code>workflow_call</code>: This defines the inputs and outputs for a reusable workflow.</p>
</li>
<li><p><code>permissions</code>: In GitHub, certain tasks need special permissions when working with the GitHub app and GitHub API. For example, for issues, <code>write</code> permission permits a user to add a comment to an issue. For other permissions, you can check out the documentation.</p>
</li>
<li><p><code>jobs</code>: The <code>jobs</code> option runs one or more jobs in your GitHub Action, each containing a set of steps that execute commands or actions.</p>
</li>
<li><p><code>runs-on</code>: The <code>runs-on</code> option defines the type of machine to run the job on.</p>
</li>
<li><p><code>steps</code>: The jobs contain a sequence of tasks called <code>steps</code>. Steps can run commands, set tasks, or an action in your repository.</p>
</li>
<li><p><code>name</code>: The name option is used to set the name of the job, which is displayed in the GitHub UI.</p>
</li>
<li><p><code>if</code>: the <code>if</code> option works similarly to an if conditional. It prevents a step from running unless a condition is met.</p>
</li>
<li><p><code>shell</code>: The <code>shell</code> option allows you to define a custom shell.</p>
</li>
<li><p><code>run</code>: The <code>run</code> option helps run commands in the operating system's shell. For example, <code>run : ls</code>, <code>run : pwd</code>, and so on.</p>
</li>
<li><p><code>uses</code>: With the <code>uses</code> option, you can run reusable units of code or other packages. You usually use it to run a GitHub package published by another developer on the <a target="_blank" href="https://github.com/marketplace">GitHub marketplace</a>. Most package developers use JavaScript or Docker container files.</p>
</li>
<li><p><code>with</code>: the <code>with</code> option accepts a value as a map with a key/value pair. It has two sub-options: <code>args</code> and an <code>entrypoint</code>. The entrypoint is used to define the entry file for Dockerfile. The args option will be passed to the container's entrypoint.</p>
</li>
</ol>
<p>You’ll typically use this syntax to create your GitHub Actions. In the next section, you’ll learn how to use it to actually build a GitHub Action.</p>
<p>For advanced GitHub Action syntax, you can <a target="_blank" href="https://docs.github.com/en/actions/writing-workflows/workflow-syntax-for-github-actions">check out the Github documentation</a>.</p>
<h2 id="heading-github-actions-examples">GitHub Actions Examples</h2>
<p>To better understand how GitHub Actions work, let’s build four examples of a GitHub Action workflow. These are common examples that many developers use and will teach you how GitHub Actions work.</p>
<h3 id="heading-node-setup">Node Setup</h3>
<p>In the following GitHub Action, we’ll set up a Node.js environment for our application. Once you’ve done that, you can test and deploy your Node.js application.</p>
<pre><code class="lang-yaml"><span class="hljs-comment"># .github/workflows/nodejs.yml</span>

<span class="hljs-attr">name:</span> <span class="hljs-string">Setup</span> <span class="hljs-string">Node.js</span> <span class="hljs-string">Env</span>

<span class="hljs-attr">on:</span>
  <span class="hljs-attr">push:</span>
    <span class="hljs-attr">branches:</span> [ <span class="hljs-string">"main"</span> ]

<span class="hljs-attr">jobs:</span>
  <span class="hljs-attr">build:</span>
    <span class="hljs-attr">runs-on:</span> <span class="hljs-string">ubuntu-latest</span>
    <span class="hljs-attr">steps:</span>
    <span class="hljs-bullet">-</span> <span class="hljs-attr">uses:</span> <span class="hljs-string">actions/checkout@v4</span>
    <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Use</span> <span class="hljs-string">Node.js</span> <span class="hljs-string">${{</span> <span class="hljs-string">matrix.node-version</span> <span class="hljs-string">}}</span>
      <span class="hljs-attr">uses:</span> <span class="hljs-string">actions/setup-node@v4</span>
      <span class="hljs-attr">with:</span>
        <span class="hljs-attr">node-version:</span>  <span class="hljs-number">21</span>
        <span class="hljs-attr">cache:</span> <span class="hljs-string">'npm'</span>
    <span class="hljs-bullet">-</span> <span class="hljs-attr">run:</span> <span class="hljs-string">npm</span> <span class="hljs-string">ci</span>
    <span class="hljs-bullet">-</span> <span class="hljs-attr">run:</span> <span class="hljs-string">npm</span> <span class="hljs-string">run</span> <span class="hljs-string">build</span> <span class="hljs-string">--if-present</span>
    <span class="hljs-bullet">-</span> <span class="hljs-attr">run:</span> <span class="hljs-string">npm</span> <span class="hljs-string">test</span>
</code></pre>
<p>For our example, we’re running our action on an Ubuntu machine. The GitHub action is triggered whenever you (or someone) push code into the repository. The <code>actions/checkout@v4</code> extension sets the <code>$GITHUB_WORKSPACE</code> environment variable to your working directory.</p>
<p>The <code>actions/setup-node@v4</code> extension sets up the Node.js environment, and the GitHub <code>run</code> option executes the Linux command.</p>
<h3 id="heading-deno-setup">Deno Setup</h3>
<p>In the following GitHub Action, we’ll set up a Deno environment for our application. You can test and analyse (using deno lint) the code for errors, stylistic issues, and so on.</p>
<pre><code class="lang-yaml"><span class="hljs-attr">name:</span> <span class="hljs-string">Deno</span>

<span class="hljs-attr">on:</span>
  <span class="hljs-attr">push:</span>
    <span class="hljs-attr">branches:</span> [<span class="hljs-string">"main"</span>]

<span class="hljs-attr">permissions:</span>
  <span class="hljs-attr">contents:</span> <span class="hljs-string">read</span>

<span class="hljs-attr">jobs:</span>
  <span class="hljs-attr">test:</span>
    <span class="hljs-attr">runs-on:</span> <span class="hljs-string">ubuntu-latest</span>

    <span class="hljs-attr">steps:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Setup</span> <span class="hljs-string">repo</span>
        <span class="hljs-attr">uses:</span> <span class="hljs-string">actions/checkout@v4</span>

      <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Setup</span> <span class="hljs-string">Deno</span>
        <span class="hljs-attr">uses:</span> <span class="hljs-string">denoland/setup-deno@v2</span>
        <span class="hljs-attr">with:</span>
          <span class="hljs-attr">deno-version:</span> <span class="hljs-string">v2.1.5</span>

      <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Run</span> <span class="hljs-string">linter</span>
        <span class="hljs-attr">run:</span> <span class="hljs-string">deno</span> <span class="hljs-string">lint</span>

      <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Run</span> <span class="hljs-string">tests</span>
        <span class="hljs-attr">run:</span> <span class="hljs-string">deno</span> <span class="hljs-string">test</span> <span class="hljs-string">-A</span>
</code></pre>
<p>For this example, we’re running our action on an Ubuntu machine. The GitHub action is triggered whenever you (or someone) push code into the repository. The <code>actions/checkout@v4</code> extension sets the <code>$GITHUB_WORKSPACE</code> environment variable to your working directory.</p>
<p>The <code>denoland/setup-deno@v2</code> extension sets up the Deno environment and the GitHub <code>run</code> option executes the Linux command.</p>
<h3 id="heading-zip-files">Zip Files</h3>
<p>In the following example, we’ll combine the <code>dist</code> folder and the <code>manifest.json</code> file into a zip archive. Then we’ll save the zipped file as an artifact for later use or download:</p>
<pre><code class="lang-yaml"><span class="hljs-attr">name:</span> <span class="hljs-string">Zip</span> <span class="hljs-string">Files</span>

<span class="hljs-attr">on:</span>
  <span class="hljs-attr">release:</span>
    <span class="hljs-attr">types:</span> [<span class="hljs-string">published</span>]

<span class="hljs-attr">jobs:</span>
  <span class="hljs-attr">zip-files:</span>
    <span class="hljs-attr">runs-on:</span> <span class="hljs-string">ubuntu-latest</span>
    <span class="hljs-attr">steps:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-attr">uses:</span> <span class="hljs-string">actions/checkout@v4</span>

      <span class="hljs-bullet">-</span> <span class="hljs-attr">uses:</span> <span class="hljs-string">vimtor/action-zip@v1.2</span>
        <span class="hljs-attr">with:</span>
          <span class="hljs-attr">files:</span> <span class="hljs-string">dist/</span> <span class="hljs-string">manifest.json</span>
          <span class="hljs-attr">dest:</span> <span class="hljs-string">build.zip</span>

       <span class="hljs-bullet">-</span> <span class="hljs-attr">uses:</span> <span class="hljs-string">actions/upload-artifact@v4</span>
         <span class="hljs-attr">with:</span>
           <span class="hljs-attr">name:</span> <span class="hljs-string">zip</span> <span class="hljs-string">file</span>
           <span class="hljs-attr">path:</span> <span class="hljs-string">${{</span> <span class="hljs-string">github.workspace</span> <span class="hljs-string">}}/build.zip</span>
</code></pre>
<p>For this example, we’re running our action on an Ubuntu machine. The GitHub Action is triggered whenever someone pushes code into the repository. The <code>actions/checkout@v4</code> extension sets the <code>$GITHUB_WORKSPACE</code> environment variable to your working directory.</p>
<p>The <a target="_blank" href="https://github.com/marketplace/actions/easy-zip-files"><code>vimtor/action-zip@v1.2</code></a> extension or package converts files into a zip folder. The <code>actions/upload-artifact@v4</code> package uploads artifacts during a workflow run.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1736875426358/0b918abf-317d-407c-a179-50693604deb7.png" alt="Upload the artifact in Github Action." class="image--center mx-auto" width="1898" height="725" loading="lazy"></p>
<h3 id="heading-deploy-a-static-website">Deploy a Static Website</h3>
<p>The following GitHub Actions example demonstrates how to deploy an HTML website to GitHub Pages.</p>
<pre><code class="lang-yaml"><span class="hljs-comment"># Simple workflow for deploying static content to GitHub Pages</span>
<span class="hljs-attr">name:</span> <span class="hljs-string">Deploy</span> <span class="hljs-string">static</span> <span class="hljs-string">content</span> <span class="hljs-string">to</span> <span class="hljs-string">Pages</span>

<span class="hljs-attr">on:</span>
  <span class="hljs-comment"># Runs on pushes targeting the default branch</span>
  <span class="hljs-attr">push:</span>
    <span class="hljs-attr">branches:</span> [<span class="hljs-string">"main"</span>]

<span class="hljs-comment"># Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages</span>
<span class="hljs-attr">permissions:</span>
  <span class="hljs-attr">contents:</span> <span class="hljs-string">read</span>
  <span class="hljs-attr">pages:</span> <span class="hljs-string">write</span>
  <span class="hljs-attr">id-token:</span> <span class="hljs-string">write</span>

<span class="hljs-comment"># Allow only one concurrent deployment, skipping runs queued between the run in-progress and latest queued.</span>
<span class="hljs-comment"># However, do NOT cancel in-progress runs as we want to allow these production deployments to complete.</span>
<span class="hljs-attr">concurrency:</span>
  <span class="hljs-attr">group:</span> <span class="hljs-string">"pages"</span>
  <span class="hljs-attr">cancel-in-progress:</span> <span class="hljs-literal">false</span>

<span class="hljs-attr">jobs:</span>

  <span class="hljs-comment"># Single deploy job since we're just deploying</span>
  <span class="hljs-attr">deploy:</span>
    <span class="hljs-attr">environment:</span>
      <span class="hljs-attr">name:</span> <span class="hljs-string">github-pages</span>
      <span class="hljs-attr">url:</span> <span class="hljs-string">${{</span> <span class="hljs-string">steps.deployment.outputs.page_url</span> <span class="hljs-string">}}</span>
    <span class="hljs-attr">runs-on:</span> <span class="hljs-string">ubuntu-latest</span>
    <span class="hljs-attr">steps:</span>

      <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Checkout</span>
        <span class="hljs-attr">uses:</span> <span class="hljs-string">actions/checkout@v4</span>

      <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Setup</span> <span class="hljs-string">Pages</span>
        <span class="hljs-attr">uses:</span> <span class="hljs-string">actions/configure-pages@v5</span>

      <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Upload</span> <span class="hljs-string">artifact</span>
        <span class="hljs-attr">uses:</span> <span class="hljs-string">actions/upload-pages-artifact@v3</span>
        <span class="hljs-attr">with:</span>
          <span class="hljs-comment"># Upload entire repository</span>
          <span class="hljs-attr">path:</span> <span class="hljs-string">'.'</span>

      <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Deploy</span> <span class="hljs-string">to</span> <span class="hljs-string">GitHub</span> <span class="hljs-string">Pages</span>
        <span class="hljs-attr">id:</span> <span class="hljs-string">deployment</span>
        <span class="hljs-attr">uses:</span> <span class="hljs-string">actions/deploy-pages@v4</span>
</code></pre>
<p>For this example, we’re running our action on an Ubuntu machine. The GitHub action is triggered whenever you push code to the repository. The <code>actions/checkout@v4</code> extension sets the <code>$GITHUB_WORKSPACE</code> environment variable to your working directory.</p>
<p>The <code>actions/configure-pages@v5</code> package helps you configure GitHub Pages and allows you to gather metadata about your website. For more detail, refer to the <a target="_blank" href="https://github.com/marketplace/actions/configure-github-pages">configure-pages action</a> documentation.</p>
<p>The <code>actions/upload-pages-artifact@v3</code> package helps you to package and upload artifacts that can be deployed to <a target="_blank" href="https://pages.github.com/">GitHub Pages.</a></p>
<p>The <code>actions/deploy-pages@v4</code> package is used to <a target="_blank" href="https://github.com/actions/deploy-pages">deploy your website</a> to GitHub Pages.</p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>Github Actions is a large topic. To more fully understand them, you can start with a basic Action example and then move on to more advanced Actions.</p>
<p>When you’re using Github Actions, the biggest problem is waiting for the results. For example, creating and updating the date on which the GitHub Action file pushes code into GitHub and then waiting for the GitHub Action result. It can be a time-consuming task, so you can use the act CLI tool instead of running GitHub Actions Locally on a laptop or computer.</p>
<p>I have published an in-depth article on freeCodeCamp on <a target="_blank" href="https://www.freecodecamp.org/news/how-to-run-github-actions-locally/">how to use the Act CLI tool</a> if you want to read more about that.</p>
<p>Thanks for reading!</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Manage your Open Source Project with GitHub ]]>
                </title>
                <description>
                    <![CDATA[ Managing your repository is one of the most important tasks for every open-source, individual, or proprietary software project. Small open-source project repositories are easily maintained without using additional functionality because few developers... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-manage-your-open-source-project-with-github/</link>
                <guid isPermaLink="false">66d99b6bf3088b4cc1a5c141</guid>
                
                    <category>
                        <![CDATA[ GitHub ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Open Source ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Rajdeep Singh ]]>
                </dc:creator>
                <pubDate>Thu, 05 Sep 2024 11:52:11 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/res/hashnode/image/upload/v1725180872053/0ea74f81-dc72-4f09-976f-4199c2e3893b.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Managing your repository is one of the most important tasks for every open-source, individual, or proprietary software project.</p>
<p>Small open-source project repositories are easily maintained without using additional functionality because few developers work on them.</p>
<p>However, when working with medium or large open-source projects, the main problem lies in how to manage them.</p>
<p>With many developers contributing at the same time and the developer community rapidly expanding day by day, this becomes a significant challenge.</p>
<p>GitHub, GitLab, Gitea, and so on, have similar functionalities that help you and your team to manage your project more efficiently. Without relying on other software and tools, you can handle your project with your repository.</p>
<p>In this tutorial, we'll discuss three basic GitHub features that can help you to manage your repository more efficiently without using any additional tools or services:</p>
<ol>
<li><p>Labels</p>
</li>
<li><p>Projects</p>
</li>
<li><p>Milestones</p>
</li>
</ol>
<p>GitHub, Gitlab, or Gitea all have similar functionalities with the same name.</p>
<h2 id="heading-how-to-use-labels-on-github">How to Use Labels on GitHub</h2>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1725176936595/be0c22c3-d182-40a9-917a-2b0b3f2ba87b.png" alt="Label example in GitHub" class="image--center mx-auto" width="1920" height="961" loading="lazy"></p>
<p>The <a target="_blank" href="https://docs.github.com/en/issues/using-labels-and-milestones-to-track-work/managing-labels">label</a> helps categorize issues, pull requests, and discussions. By default, GitHub comes with some built-in labels.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1725177106112/5ceb58b0-985b-4582-bb67-e4313dd833b4.png" alt="Default Github label list" class="image--center mx-auto" width="1920" height="961" loading="lazy"></p>
<p>You can also create a custom label. You can use the label on any issue, pull request, or discussion within your repository.</p>
<p>You can find the list of <a target="_blank" href="https://docs.github.com/en/issues/using-labels-and-milestones-to-track-work/managing-labels#about-default-labels">default labels in the GitHub documentation</a>.</p>
<h3 id="heading-how-to-create-a-label-in-your-repository">How to Create a Label in Your Repository</h3>
<p>Creating a custom label in the repository is very simple. There are different ways to make a label. You need to follow these common steps:</p>
<p>Go to your repository &gt; then go to issues &gt; then click on the labels button.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1725177276807/85a102cf-0817-4d96-a353-6675272f6d0b.png" alt="Go to Label Page." class="image--center mx-auto" width="1920" height="961" loading="lazy"></p>
<p>Next, click on the new label button and enter your label name, description and color.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1725177420526/1a6b3752-ce12-4d01-a241-4fe469ec9f44.png" alt="Create the label in Github." class="image--center mx-auto" width="1920" height="961" loading="lazy"></p>
<h3 id="heading-how-to-delete-and-edit-labels-on-github">How to Delete and Edit Labels on GitHub</h3>
<p>To edit and delete a label, go to the issue page and click on the <strong>Label</strong> button. In the label page, you should see all the existing labels.</p>
<p>Click on the <strong>Edit</strong> button to edit a label, and click on the <strong>Delete</strong> button to delete a label.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1725177545826/62926118-df86-4e82-ab05-539ca14c136b.png" alt="Edit and delete the Label" class="image--center mx-auto" width="1920" height="961" loading="lazy"></p>
<blockquote>
<p>You cannot delete multiple labels with GitHub.</p>
</blockquote>
<h2 id="heading-how-to-use-github-projects">How to Use GitHub Projects</h2>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/08/project.png" alt="Project page" class="image--center mx-auto" width="600" height="400" loading="lazy"></p>
<p>The <a target="_blank" href="https://docs.github.com/en/issues/planning-and-tracking-with-projects">Github Projects</a> tool is a versatile and flexible tool for planning and managing your repository work in one central location.</p>
<p>It functions similarly to a spreadsheet, task board, and road map, allowing you to plan and track your repository work in one place. The GitHub project is fully integrated with GitHub.</p>
<p>You can create and customize multiple views, filter, sort, and group your issues and pull requests, visualize work with charts, and add custom fields to track specific metadata.</p>
<p>You can assign users to specific issues, check issue statuses, and assign reviewers, among other functions.</p>
<p>GitHub projects come in two types: public and private.</p>
<ul>
<li><p><strong>Public</strong> projects are visible to everyone, and the management team can make edits and changes to the project.</p>
</li>
<li><p><strong>Private</strong> projects, on the other hand, are not visible to others, and only the management team can edit and make changes to the project.</p>
</li>
</ul>
<p>By default, projects are private in GitHub.</p>
<h3 id="heading-how-to-create-projects-on-github">How to Create Projects on GitHub</h3>
<p>Creating a project is a straightforward task. In some cases, the projects tab may not be visible on your repository. First, go to your repository section and enable the project.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1725178051832/fac9c92e-8527-4c28-94ea-daf102a6ba93.png" alt="Enable the Project Tab in the Github." class="image--center mx-auto" width="1920" height="961" loading="lazy"></p>
<p>After enabling projects on your repository, you should now be able to see a projects tab in your repository. Now click on the projects tab.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1725178167552/383a3f8d-d4e6-4994-a4f3-c556bc4e25f4.png" alt="Click on the Project Tab" class="image--center mx-auto" width="1920" height="961" loading="lazy"></p>
<p>Your default project page looks like this. To create a new project, click on the dropdown icon and select the <strong>New Project</strong>, then click on the <strong>New Project</strong> option.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1725178437699/eec3dbff-8715-4d3b-805c-83cdfb149c06.png" alt="Create a new project" class="image--center mx-auto" width="1920" height="961" loading="lazy"></p>
<p>Next, select the templates for the project according to your requirements. Click on the <strong>view all</strong> button to view all available templates, or go with blank templates.</p>
<p>The template comes with pre-configuration based on what you choose.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1725178566692/29932135-3e90-42d8-8b71-092eac00d9ea.png" alt="Select the template for your project." class="image--center mx-auto" width="1920" height="961" loading="lazy"></p>
<p>I selected the feature release template for this tutorial. Next, enter your project name and click on the <strong>Create Project</strong> button.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1725178693346/211597d1-309d-4ccf-a0bd-c6fbed891111.png" alt="Create a project with the selected template." class="image--center mx-auto" width="1920" height="961" loading="lazy"></p>
<p>Your project should be created based on the template you chose. Your project dashboard page may be different according to your template.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1725179016256/91a10481-874b-4441-9038-0686675ca7ae.png" alt="Your project dashboard looks like this." class="image--center mx-auto" width="1920" height="961" loading="lazy"></p>
<h3 id="heading-how-to-delete-and-edit-projects-on-github">How to Delete and Edit Projects on GitHub</h3>
<p>To edit and delete a project, go to the project page.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1725179133210/2526dec0-e354-48db-b6a9-a607f644ccf7.png" alt="GitHub projects page" class="image--center mx-auto" width="1920" height="961" loading="lazy"></p>
<p>Then click on a project which you want to edit or delete. Clicking on the project title should take you to the project setting page.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1725179213957/ef9aa361-3f2b-41d5-bb4a-660886120f9b.png" alt="Project page" class="image--center mx-auto" width="1920" height="961" loading="lazy"></p>
<p>In the project settings page, you can edit the project title and description, delete the project, close the project and also change the visibility of your project from private to public.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1725179309181/5533fb7a-9dc5-42c1-81ae-9b1ee7a60b46.png" alt="Project settings page" class="image--center mx-auto" width="1920" height="961" loading="lazy"></p>
<h2 id="heading-how-to-use-milestones-on-github">How to Use Milestones on GitHub</h2>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1725179667880/661d75ba-1212-4987-a4db-1344cf8fb487.png" alt="Milestones in Github" class="image--center mx-auto" width="1920" height="961" loading="lazy"></p>
<p>The <a target="_blank" href="https://docs.github.com/en/issues/using-labels-and-milestones-to-track-work/about-milestones">milestone feature</a> allows you to track the progress of issues or pull requests in a repository. With milestones, you can prioritize open issues and pull requests and set a due date for a group of related items.</p>
<p>In simple terms, milestones operate like a to-do list, where you can detail the amount of completed or pending work. It functions like a progress bar, helping your team manage the project more effectively and communicate the importance of specific issues to both your team and the open-source community.</p>
<p>Milestones allows the open-source community and your team to understand the status of completed or pending work, and the timeline for upcoming versions.</p>
<h3 id="heading-how-to-create-milestones-on-github">How to Create Milestones on GitHub</h3>
<p>First, go to the repository and then to the issue page. Click on the milestones button.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1725179807807/d9b3aeeb-b244-4165-af99-10eb395d5e71.png" alt="Click on the Milestones button." class="image--center mx-auto" width="1920" height="961" loading="lazy"></p>
<p>Now, you should see your exciting milestones list. To create milestones, click on the <strong>new milestones</strong> button.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1725179917831/f4e186c6-3bf7-4058-8de4-9a3d470c96ca.png" alt="Click on the New Milestones button to create a Milestone." class="image--center mx-auto" width="1920" height="961" loading="lazy"></p>
<p>Enter the milestone title, due date and description and last, then click on the <strong>Create milestones</strong> button to make your milestone.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1725180035699/e3a7d784-a33c-45a0-a2d0-4cd53d33a251.png" alt="Create a new milestone in Github" class="image--center mx-auto" width="1920" height="961" loading="lazy"></p>
<p>By default, your created milestone should look like this:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/08/bydefault-your-milestone-look-like-this.png" alt="Enter information about milestones." class="image--center mx-auto" width="600" height="400" loading="lazy"></p>
<p>Our next task is to assign an issue to the milestone. Go to the issue and assign a milestone to it.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1725180104276/ad15123a-acf9-4577-90cb-3aa294f03d8a.png" alt="Assign the milestone into a issue" class="image--center mx-auto" width="1920" height="961" loading="lazy"></p>
<p>If you have multiple milestones<a target="_blank" href="https://docs.github.com/en/issues/using-labels-and-milestones-to-track-work/about-milestones">,</a> you can select one of them.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1725180261186/4e506d15-613b-4d10-818c-2ff8168a6459.png" alt="Select the milestone" class="image--center mx-auto" width="1920" height="961" loading="lazy"></p>
<p>Verify whether the issue is attached or not to the milestone.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1725180354959/4c7a3127-df19-49bb-89dc-3409eae8e23a.png" alt="Verify whether the issue is attached or not to the milestone." class="image--center mx-auto" width="1920" height="961" loading="lazy"></p>
<p>Now, you can attach or assign multiple issues with a single milestone. When you visit the milestone, you can see all the assigned milestone lists.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1725180446204/149dc05e-8e6f-4db1-816d-8b1b179b5a47.png" alt="Assigned milestone lists." class="image--center mx-auto" width="1920" height="961" loading="lazy"></p>
<p>If you and your teammate close the issue, your progress bar is increased automatically. This can help your team and your community understand how much work has been completed.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1725180561440/6133c4c6-e60f-4176-88dc-80beb916ea30.png" alt="The progress bar increased automatically in milestones." class="image--center mx-auto" width="1920" height="961" loading="lazy"></p>
<h3 id="heading-how-to-delete-and-edit-milestones-on-github">How to Delete and Edit Milestones on GitHub</h3>
<p>To edit and delete the milestones, you need to go to the <strong>issues page</strong> and click on the milestones button to see the available milestones.</p>
<p>To edit milestones, click on the <strong>edit</strong> button, and to delete the milestones, click on the <strong>delete</strong> button.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1725180673832/1b71ecd9-fadb-4feb-9b62-2c1a4fcf597c.png" alt="Edit and delete the milestone" class="image--center mx-auto" width="1920" height="961" loading="lazy"></p>
<h1 id="heading-conclusion">Conclusion</h1>
<p>Labels, projects, and milestones are basic features useful for managing a project on GitHub. When you use them, you automatically learn about them.</p>
<p>Both milestones and projects are different, and you can not compare both at the same time because both provide different functionalities and working.</p>
<p>As I mentioned, a milestone is used to allow you to track the progress of issues or pull requests in a repository. On the other hand, a project is used to plan and manage your repository from one central location.</p>
<p>For small teams, I recommend milestones, and for the large team, I recommend using projects. You can also use both projects and milestones for better productivity.</p>
<p>I've written other article related to GitHub, and you can check them out here:</p>
<ul>
<li><p><a target="_blank" href="https://www.freecodecamp.org/news/what-is-github-wiki-and-how-do-you-use-it/">https://www.freecodecamp.org/news/what-is-github-wiki-and-how-do-you-use-it/</a></p>
</li>
<li><p><a target="_blank" href="https://www.freecodecamp.org/news/github-flavored-markdown-syntax-examples/">https://www.freecodecamp.org/news/github-flavored-markdown-syntax-examples/</a></p>
</li>
<li><p><a target="_blank" href="https://www.freecodecamp.org/news/how-to-run-github-actions-locally/">https://www.freecodecamp.org/news/how-to-run-github-actions-locally/</a></p>
</li>
</ul>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ What is a GitHub Wiki and How Do You Use it? ]]>
                </title>
                <description>
                    <![CDATA[ A GitHub wiki is a great place for your project's documentation. You can use the wiki to create, manage, and host documentation for your repository so others can use and contribute to your project. GitHub wikis are easy to start using without install... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/what-is-github-wiki-and-how-do-you-use-it/</link>
                <guid isPermaLink="false">66d038c3ccf811d3117aef0c</guid>
                
                    <category>
                        <![CDATA[ documentation ]]>
                    </category>
                
                    <category>
                        <![CDATA[ GitHub ]]>
                    </category>
                
                    <category>
                        <![CDATA[ version control ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Rajdeep Singh ]]>
                </dc:creator>
                <pubDate>Mon, 15 Apr 2024 20:46:02 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2024/04/What-is-GitHub-Wiki-and-How-Do-You-Use-it.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>A GitHub wiki is a great place for your project's documentation. You can use the wiki to create, manage, and host documentation for your repository so others can use and contribute to your project.</p>
<p>GitHub wikis are easy to start using without installing any other software. The best part is that the wiki is integrated with your GitHub repository. </p>
<p>You do not need any other tool – you just need to know how to use markdown, as you'll use it to write your wiki. (You can <a target="_blank" href="https://www.freecodecamp.org/news/github-flavored-markdown-syntax-examples/">read all about that in my other article here</a>.)</p>
<h2 id="heading-how-to-start-using-github-wiki">How to Start Using GitHub Wiki</h2>
<p>You can start your GitHub wiki with just one click. Every GitHub repository has a Wiki tab in the menu at the top of the page. To start, click on it.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/04/github-wiki.png" alt="GitHub repository Page" width="600" height="400" loading="lazy">
<em>GitHub repository Page</em></p>
<p>The wiki tab is sometimes not shown by default in the GitHub repository nav bar. First, you'll need to enable wikis in your repository settings.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/04/no-wiki.png" alt="No wiki tab is show." width="600" height="400" loading="lazy">
<em>No wiki tab is shown.</em></p>
<p>To do that, go to your repository settings page, scroll down, and find the features section. Then enable wikis by checking the "Wikis" box.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/04/Wiki-enable.png" alt="Enable wiki.in GitHub" width="600" height="400" loading="lazy">
<em>Enable wiki</em></p>
<p>To initialize the wiki in your GitHub repository, create the home page in your wiki.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/04/first-page.png" alt="Initialize the wiki in GitHub." width="600" height="400" loading="lazy">
<em>Initialize the wiki in GitHub.</em></p>
<p>When you click the "<strong>Create the first page</strong>" button, you'll be redirected to the editor page where you can create a home page in the wiki.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/04/Home-wiki-Page.png" alt="Create a home page and initialize the wiki." width="600" height="400" loading="lazy">
<em>Create a home page and initialize the wiki.</em></p>
<p>Your wiki home page should now look like this:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/04/Your-wiki-is-look-like-this.png" alt="Image" width="600" height="400" loading="lazy">
<em>Wiki home page</em></p>
<h2 id="heading-how-to-clone-a-github-wiki-locally">How to Clone a GitHub Wiki Locally</h2>
<p>Sometimes, new developers are confused about how to clone the wiki locally. To do this, just copy the link where it says “Clone this wiki locally”, as you can see in the image below:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/04/clone.png" alt="Copy the link to clone the GitHub wiki." width="600" height="400" loading="lazy">
<em>Copy the link to clone the GitHub wiki.</em></p>
<p>Copy that link and clone the GitHub wiki repository locally on your laptop or machine.</p>
<p>Now, you can make changes in the wiki, such as editing, updating, or changing documentation locally. After you finish any documentation changes, you can push your local wiki documentation to the GitHub wiki repository.</p>
<pre><code class="lang-bash">$ git <span class="hljs-built_in">clone</span> https://github.com/officialrajdeepsingh/github-wiki-tutorial.wiki.git
Cloning into <span class="hljs-string">'github-wiki-tutorial.wiki'</span>...
remote: Enumerating objects: 6, <span class="hljs-keyword">done</span>.
remote: Counting objects: 100% (6/6), <span class="hljs-keyword">done</span>.
remote: Compressing objects: 100% (2/2), <span class="hljs-keyword">done</span>.
remote: Total 6 (delta 1), reused 0 (delta 0), pack-reused 0
Receiving objects: 100% (6/6), <span class="hljs-keyword">done</span>.
Resolving deltas: 100% (1/1), <span class="hljs-keyword">done</span>.
</code></pre>
<h2 id="heading-how-to-customize-your-wiki">How to Customize Your Wiki</h2>
<p>Wikis have limited customization options for the sidebar, home page, and footer. But you can extend these options using HTML, CSS, and Markdown.</p>
<p>We have already discussed the home page, and now we'll discuss the footer and sidebar.</p>
<p>The footer and sidebar show or contain helpful links such as contact information, navigation links, social media links, and so on.</p>
<p>The footer is shown at the bottom of every page on your site, and the sidebar is typically a vertical column on the left or right side of a web page. Both are visible on all pages of the wiki.</p>
<h3 id="heading-how-to-create-a-custom-sidebar">How to create a custom sidebar</h3>
<p>There are two ways to create a sidebar in the GitHub wiki.</p>
<ol>
<li>With the GitHub UI</li>
<li>Locally in your IDE</li>
</ol>
<p>We'll look at each method here so you can choose the one that works best for you.</p>
<h4 id="heading-with-the-github-ui">With the GitHub UI</h4>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/04/sidebar-1.png" alt="Create a sidebar" width="600" height="400" loading="lazy">
<em>Create a sidebar</em></p>
<p>Go to the wiki home page and click on the “Add a custom sidebar” button to create a sidebar in your wiki.</p>
<p>Next, it will redirect you to the editor page to create a sidebar page. In the sidebar file, you can write markdown content such as navigation links, and so on. After that, click the <strong>save button</strong>.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/04/create-sidebar-page.png" alt="Create _Sidebar.md file in github wiki." width="600" height="400" loading="lazy">
_Create <code>_Sidebar.md</code> file._</p>
<h4 id="heading-locally-in-your-ide">Locally in your IDE</h4>
<p>The second way is to clone your wiki locally and then create a <code>_Sidebar.md</code><br>file in your wiki at the root level using VS Code or any other IDE which you like. </p>
<h3 id="heading-how-to-create-a-custom-footer">How to create a custom footer</h3>
<p>You'll follow basically the same steps as in the sidebar section to create your custom wiki footer.</p>
<h4 id="heading-with-the-github-ui-1">With the GitHub UI</h4>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/04/footer.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>Go to your wiki page and click on the “Add a custom footer” button to create a footer in your wiki.</p>
<p>Next, it will redirect you to the editor page to create a footer. In the footer file, you can write markdown content such as navigation links, and so on. After that, click the <strong>save button</strong>.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/04/footer-editor.png" alt="Image" width="600" height="400" loading="lazy">
<em>Create a footer</em></p>
<h4 id="heading-locally-in-your-ide-1">Locally in your IDE</h4>
<p>The second way is to clone your wiki locally and then create a <code>_Footer.md</code><br>file in your wiki at the root level using VS Code or any other IDE which you like. </p>
<h2 id="heading-what-is-a-page-how-do-you-create-a-new-page-in-the-wiki">What is a Page? How Do You Create a New Page in the Wiki?</h2>
<p>In a wiki, a page has functionality similar to other CMSs, giving you the power to manage your content and documentation.</p>
<p>With the wiki page, you can divide your content or docs into different sections such as installation, configuration, and so on.</p>
<p>To create a new page, click on the new page button.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/04/create-a-page.png" alt="Create a new wiki page" width="600" height="400" loading="lazy">
<em>Create a new wiki page</em></p>
<p>It redirects you to the editor page, where you can add a title and content. After your writing is finished, click on the save button.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/04/create-a-new-page-with-wiki.png" alt="Create a wiki page" width="600" height="400" loading="lazy">
<em>Create a wiki page</em></p>
<p>Your page looks like this in the wiki after it is published:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/04/your-installation-page.png" alt="The Wiki page looks like this in the browser after publishing." width="600" height="400" loading="lazy">
<em>The Wiki page looks like this in the browser after publishing.</em></p>
<p>Everybody can access your pages section. Every page you publish is shown in the pages section on your wiki.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/04/access-the-page.png" alt="The page section shows the published page list." width="600" height="400" loading="lazy">
<em>The page section shows the published page list.</em></p>
<h2 id="heading-how-to-enable-and-disable-collaboration-in-the-wiki">How to Enable and Disable Collaboration in the Wiki</h2>
<p>To enable collaboration for everyone in the wiki, go to your GitHub repository settings page, scroll down, find the features, and unselect the "Restrict editing to collaborators only" checkbox.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/04/access-to-eveyone-in-wiki.png" alt="Enable Collaboration in the GitHub Wiki." width="600" height="400" loading="lazy">
<em>Enable Collaboration in the GitHub Wiki.</em></p>
<p>You can turn off collaboration for everyone, so that you and your team are the only ones responsible for updating, deleting, and editing the wiki.</p>
<p>To do this, you need to restrict editing for other users. Enabling the <strong>Restrict editing to collaborators only</strong> option quickly accomplishes this.</p>
<p>After there haven't been any edits, invite your team and give them access to it. Then just click the "Restrict editing to collaborators only" checkbox.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/04/enable-it.png" alt="Disable Collaboration in the GitHub Wiki." width="600" height="400" loading="lazy">
<em>Disable Collaboration in the GitHub Wiki.</em></p>
<h2 id="heading-why-is-github-wiki-so-useful">Why is GitHub Wiki So Useful?</h2>
<p>GitHub wikis can be useful for everyone. You can start your documentation with a wiki in less than one minute. You do not need anything to start writing your documentation except basic knowledge of Markdown syntax. </p>
<p>Using GitHub wikis, you can just focus on writing basic documentation and on the project itself. GitHub wiki handles the rest of your documentation such as hosting concerns, search, and so on. Most importantly, for public repository wikis, it's <a target="_blank" href="https://docs.github.com/en/communities/documenting-your-project-with-wikis/adding-or-editing-wiki-pages">totally free</a>. </p>
<p>Many famous open-source projects use Wiki nowadays, such as <a target="_blank" href="https://github.com/facebook/hhvm/wiki">hhvm</a>, <a target="_blank" href="https://github.com/neovim/neovim/wiki">neovim</a>, <a target="_blank" href="https://github.com/guard/guard/wiki">guard</a>, <a target="_blank" href="https://github.com/apple/foundationdb/wiki">foundation db</a>, and others.</p>
<p>Check out the <a target="_blank" href="https://github.com/MyHoneyBadger/awesome-github-wiki">list of projects used in Wiki</a>.</p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>There are many documentation frameworks on the market, such as Nextra, Lume, Starlight, and Docusaurus. But they take some time to learn, configure, and set up. </p>
<p>Also, if you're still working on your coding skills and you aren't comfortable with tools like React, MDX, and so on, you'll need to take some time to learn them before using these more advanced documentation frameworks.</p>
<p>With GitHub Wiki, you can start creating your documentation right away, and you do not need to worry about deploying and hosting anything managed by GitHub.</p>
<p>GitHub Wiki is a great choice for small and early-stage projects. You and your team can focus on the project while composing straightforward documentation.</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ Learn GitHub-Flavored Markdown Syntax and Formatting – With Examples ]]>
                </title>
                <description>
                    <![CDATA[ Markdown is a lightweight, open-source, easy-to-read and easy-to-write method of formatting text that you can use as plain text in any IDE or editor. When writing on GitHub, you can use Markdown syntax and HTML elements to extend Markdown's functiona... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/github-flavored-markdown-syntax-examples/</link>
                <guid isPermaLink="false">66d038a57d662cf201f5ec59</guid>
                
                    <category>
                        <![CDATA[ GitHub ]]>
                    </category>
                
                    <category>
                        <![CDATA[ markdown ]]>
                    </category>
                
                    <category>
                        <![CDATA[ syntax ]]>
                    </category>
                
                    <category>
                        <![CDATA[ technical writing ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Rajdeep Singh ]]>
                </dc:creator>
                <pubDate>Thu, 11 Apr 2024 19:08:04 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2024/04/freecodecampl-github.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p><a target="_blank" href="https://en.wikipedia.org/wiki/Markdown">Markdown</a> is a lightweight, open-source, easy-to-read and easy-to-write method of formatting text that you can use as plain text in any IDE or editor.</p>
<p>When writing on GitHub, you can use Markdown syntax and HTML elements to extend Markdown's functionality. You can use Markdown syntax everywhere in GitHub, such as in the README file, wiki, comments, pull requests, and when creating issues.</p>
<p>For every software developer, learning markdown is an essential step along the path of your career.</p>
<p>To enhance Markdown's basic features, GitHub added some custom functionalities and created <a target="_blank" href="https://docs.github.com/en/get-started/writing-on-github/getting-started-with-writing-and-formatting-on-github/about-writing-and-formatting-on-github">GitHub-Flavored Markdown</a>. With this, you can easily interact with other users in pull requests and issues by mentioning user, issue, and PR references and adding emoji.</p>
<p>This tutorial teaches you the basics of GitHub-Flavored Markdown so you can start using it in your projects.</p>
<p>All the code is <a target="_blank" href="https://github.com/officialrajdeepsingh/github-tutorial">available in the GitHub repository</a>.</p>
<h2 id="heading-github-flavored-markdown-syntax">GitHub-Flavored Markdown Syntax</h2>
<p>GitHub Flavored Markdown syntax is divided into two parts.</p>
<ol>
<li><a class="post-section-overview" href="#basis-formatting-syntax">Basic Formatting Syntax</a></li>
<li><a class="post-section-overview" href="#heading-advanced-formatting-syntax">Advanced Formatting Syntax</a></li>
</ol>
<p>We'll look at each one in detail below.</p>
<h3 id="heading-basic-formatting-syntax">Basic Formatting Syntax</h3>
<p>Basic formatting syntax applies to everyone. It contains fundamental essentials such as headings, code, images, quotes, links, and so on – things you'll need to know for writing.</p>
<ol>
<li><a class="post-section-overview" href="#heading-headings">Headings</a></li>
<li><a class="post-section-overview" href="#heading-paragraphs">Paragraphs</a></li>
<li><a class="post-section-overview" href="#comment">Comment</a></li>
<li><a class="post-section-overview" href="#heading-styling-text">Styling text</a></li>
<li><a class="post-section-overview" href="#heading-quotes">Quotes</a></li>
<li><a class="post-section-overview" href="#heading-code">Code</a></li>
<li><a class="post-section-overview" href="#heading-links">Links</a></li>
<li><a class="post-section-overview" href="#heading-images">Images</a></li>
<li><a class="post-section-overview" href="#heading-lists">Lists</a></li>
<li><a class="post-section-overview" href="#heading-mentioning-people-and-teams">Mentioning people and teams</a></li>
<li><a class="post-section-overview" href="#heading-referencing-issues-and-pull-requests">Referencing issues and pull requests</a></li>
<li><a class="post-section-overview" href="#using-emojis">Using emojis</a></li>
<li><a class="post-section-overview" href="#heading-footnotes">Footnotes</a></li>
<li><a class="post-section-overview" href="#heading-alerts">Alerts</a></li>
</ol>
<p>Note that the code samples mostly come from <a target="_blank" href="https://docs.github.com/en/get-started">GitHub's documentation</a>.</p>
<h3 id="heading-headings">Headings</h3>
<p>You can use the <code>#</code> symbol to create headings. One <code>#</code> creates an H1 heading, two create an H2 heading, and so on, like this:</p>
<pre><code class="lang-markdown"><span class="hljs-section"># A first-level heading</span>
<span class="hljs-section">## A second-level heading</span>
<span class="hljs-section">### A third-level heading</span>
<span class="hljs-section">#### A four-level heading</span>
<span class="hljs-section">##### A five-level heading</span>
<span class="hljs-section">###### A six-level heading</span>
</code></pre>
<h3 id="heading-paragraphs">Paragraphs</h3>
<p>To create paragraphs, you can use a blank line to separate one or more lines of text or paragraphs.</p>
<pre><code class="lang-markdown">Lorem ipsum dolor sit amet, consectetur adipiscing elit. Etiam est odio, commodo id diam sed, pulvinar sagittis tortor. Nam vestibulum purus eros. Sed congue, mi id pretium auctor, nibh augue iaculis arcu, eu tristique quam dolor at erat.

Quisque vel odio condimentum, mollis sem vitae, porta diam. Praesent ligula elit, condimentum eget ex sed, commodo sollicitudin sapien.


Proin volutpat faucibus nulla. Nullam eros sem, ultricies gravida nunc nec, dapibus posuere nisl. Nunc lacinia elementum turpis in pharetra. Aenean eu neque eros.
</code></pre>
<h3 id="heading-comments">Comments</h3>
<p>Comments are available in almost every programming language. They help developers write notes and add additional information to their code, helping other developers understand what's going on and how the code is working.</p>
<p>To add notes and additional information in Markdown, use the following syntax: <code>&lt;!--- Wrap text ---&gt;</code>.</p>
<p>Here's an example:</p>
<pre><code class="lang-markdown"><span class="xml"><span class="hljs-comment">&lt;!-- This content will not appear in the rendered Markdown --&gt;</span></span>
</code></pre>
<h3 id="heading-styling-text">Styling text</h3>
<p>You can apply basic styles to your text, such as bold, italic, strikethrough, subscript, or superscript, to improve readability and convey your point more clearly.</p>
<ol>
<li>For <strong>Bold</strong>, you can use the following syntax:  <code>**your text**</code></li>
<li>For <strong><em>italics</em></strong>, you can use the following syntax:  <code>*your text* or _your text_.</code></li>
<li>For <strong>strikethrough</strong>, you can use the following syntax: <code>~~your text~~</code></li>
<li>For <strong>subscript</strong>, you can use the following syntax:  <code>The subscript &lt;sub&gt; text &lt;/sub&gt; is here.</code></li>
<li>For <strong>superscript</strong>, you can use the following syntax:  <code>The superscript &lt;sup&gt; text &lt;/sup&gt; is here.</code></li>
</ol>
<pre><code class="lang-markdown"><span class="hljs-section">## Bold</span>

<span class="hljs-strong">**your text**</span>

<span class="hljs-section">## italics</span>

<span class="hljs-emphasis">*your text*</span>
<span class="hljs-emphasis">_your text_</span>

<span class="hljs-section">## strikethrough</span>

~~your text~~

<span class="hljs-section">## subscript</span>

The subscript <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">sub</span>&gt;</span></span> text <span class="xml"><span class="hljs-tag">&lt;/<span class="hljs-name">sub</span>&gt;</span></span> is here.

<span class="hljs-section">## superscript</span>

The subscript <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">sup</span>&gt;</span></span> text <span class="xml"><span class="hljs-tag">&lt;/<span class="hljs-name">sup</span>&gt;</span></span> is here.
</code></pre>
<h3 id="heading-quotes">Quotes</h3>
<p>A blockquote or quote is a sentence or paragraph formatted to let the reader know that you're quoting someone. To create a blockquote in Markdown, you can use the <code>&gt;</code> symbol.</p>
<pre><code class="lang-markdown"><span class="hljs-quote">&gt; Text that is a quote</span>
</code></pre>
<h3 id="heading-code">Code</h3>
<p>Markdown files support two types of code samples: <strong>inline</strong> and <strong>code block</strong>.</p>
<ol>
<li>To add a code block in a Markdown file, use the following syntax: <code>``` your code ``` </code>.</li>
<li>To add inline code to the Markdown file, use the following syntax: <code>`your code` </code>.</li>
</ol>
<pre><code class="lang-markdown"><span class="hljs-section">## Code Block</span>
</code></pre>
<p>// ES5 syntax
var multiply = function(x, y) {
  return x * y;
};</p>
<p>// ES6 arrow function
var multiply = (x, y) =&gt; { return x * y; };</p>
<p>// Or even simpler
var multiply = (x, y) =&gt; x * y;</p>
<pre><code>
## Inline code 

JavaScript provides three different value comparison operations: strict equality using <span class="hljs-string">`===`</span>, loose equality using <span class="hljs-string">`==`</span>, and the <span class="hljs-string">`Object.is()`</span> method.
</code></pre><p>To support code highlighting in a code block, you can add an optional language identifier after your triple backticks (like JavaScript in the example below):</p>
<pre><code class="lang-markdown"><span class="hljs-section">## Code Block</span>

<span class="hljs-code">```javascript

// ES5 syntax
var multiply = function(x, y) {
  return x * y;
};

// ES6 arrow function
var multiply = (x, y) =&gt; { return x * y; };

// Or even simpler
var multiply = (x, y) =&gt; x * y;</span>
</code></pre>
<pre><code>
### Links

A markdown file divides links into two categories: **inline** and **relative**.

#### Inline links

To create an inline link <span class="hljs-keyword">in</span> a Markdown file, wrap the link text <span class="hljs-keyword">in</span> brackets <span class="hljs-string">`[ ]`</span> followed immediately by the URL <span class="hljs-keyword">in</span> parentheses <span class="hljs-string">`( )`</span>.

<span class="hljs-string">``</span><span class="hljs-string">`markdown
This site was built using [GitHub Pages](https://pages.github.com/).</span>
</code></pre><h4 id="heading-relative-links">Relative links</h4>
<p>Relative links are defined similarly to inline links but they change in the <code>[]</code> section: the <code>[]</code> section contains the path of the file in your repository. </p>
<p>You use relative links to link two files: for example, to link the CONTRIBUTING file into the README file.</p>
<pre><code class="lang-markdown">[<span class="hljs-string">Contribution guidelines</span>](<span class="hljs-link">docs/CONTRIBUTING.md</span>)
</code></pre>
<p>Relative links starting with <code>/</code> will be relative to the repository root. You can use all relative link operands, such as <code>./</code> and <code>../.</code>:</p>
<pre><code class="lang-markdown">[<span class="hljs-string">Contribution guidelines</span>](<span class="hljs-link">../docs/CONTRIBUTING.md</span>)
</code></pre>
<h3 id="heading-images">Images</h3>
<p>To add an image in a markdown file, add a <code>!</code> and then wrap the alt text in <code>[]</code>. Then, wrap the image link with parentheses <code>()</code>.</p>
<p>It looks like this:</p>
<pre><code class="lang-markdown">![<span class="hljs-string">Markdown</span>](<span class="hljs-link">https://img.shields.io/badge/markdown-%23000000.svg?style=for-the-badge&amp;logo=markdown&amp;logoColor=white</span>)
</code></pre>
<h3 id="heading-lists">Lists</h3>
<p>A list helps record essential information in order, which can be vital for the reader and makes it easy for people to understand and find information. </p>
<p>Markdown files support three types of lists:</p>
<ol>
<li>Ordered list</li>
<li>Unordered list</li>
<li>Task list</li>
</ol>
<h4 id="heading-ordered-list">Ordered list</h4>
<p>The first type is an ordered list. To create an ordered list, start with numbers followed by periods.</p>
<pre><code class="lang-markdown"><span class="hljs-bullet">1.</span> one
<span class="hljs-bullet">2.</span> two
<span class="hljs-bullet">3.</span> three
<span class="hljs-bullet">4.</span> four
</code></pre>
<h4 id="heading-unordered-list">Unordered list</h4>
<p>The second type is an unordered list. To create an unordered list, use <code>-</code>, <code>+</code> or <code>*</code> (depending on your preference - they'll all render as an unordered list):</p>
<pre><code class="lang-markdown"><span class="hljs-bullet">*</span> First item
<span class="hljs-bullet">*</span> Second item
<span class="hljs-bullet">*</span> Third item
<span class="hljs-bullet">*</span> Fourth item


<span class="hljs-bullet">-</span> First item
<span class="hljs-bullet">-</span> Second item
<span class="hljs-bullet">-</span> Third item
<span class="hljs-bullet">-</span> Fourth item

<span class="hljs-bullet">+</span> First item
<span class="hljs-bullet">+</span> Second item
<span class="hljs-bullet">+</span> Third item
<span class="hljs-bullet">+</span> Fourth item
</code></pre>
<h4 id="heading-task-list">Task list</h4>
<p>The third type is a task list. To create a task list, list items start with a hyphen, followed by a space, followed by square brackets <code>[]</code>. You can use an <code>x</code> in the bracket <code>[x]</code> to mark a task as complete.</p>
<pre><code class="lang-markdown"><span class="hljs-bullet">-</span> [x] #739
<span class="hljs-bullet">-</span> [ ] https://github.com/octo-org/octo-repo/issues/740
<span class="hljs-bullet">-</span> [ ] Add delight to the experience when all tasks are complete :tada:
</code></pre>
<h3 id="heading-mentioning-people-and-teams">Mentioning people and teams</h3>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/04/username-in-github.png" alt="Mention users and teams in the markdown" width="600" height="400" loading="lazy">
<em>Mentioning users and teams in markdown</em></p>
<p>To mention a person or team in a GitHub markdown file, type <code>@</code>  and write the username or team username.</p>
<pre><code class="lang-markdown"><span class="hljs-section">## person or individual username</span>

@officialrajdeepsingh, check out the following change.

<span class="hljs-section">## Team or company</span>
The section blog theme is maintained by @frontendweb
</code></pre>
<h3 id="heading-referencing-issues-and-pull-requests">Referencing issues and pull requests</h3>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/04/tag-and-refrence-with-markdown.png" alt="Issues and pull request" width="600" height="400" loading="lazy">
<em>Issues and pull requests</em></p>
<p>To mention issues and pull requests in a GitHub markdown file, type a <code>#</code>, then type the issue or pull request number or title. Then press either tab or enter to complete the highlighted result.</p>
<pre><code class="lang-markdown">Remove the default <span class="hljs-emphasis">_target blank in logo #93</span>
</code></pre>
<h3 id="heading-using-emoji">Using emoji</h3>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/04/emoji-in-markdown.png" alt="Adding emoji in markdown." width="600" height="400" loading="lazy">
<em>Adding emoji in markdown.</em></p>
<p>To add an emoji to your writing, type the emoji's code between two colons. If you just type <code>:</code>, a list of suggested emojis on GitHub will appear. </p>
<p>Once you find the emoji you're looking for, press Tab or Enter to choose the highlighted result.</p>
<pre><code class="lang-markdown">Don't forget to leave a star on our repository! :star:
</code></pre>
<h3 id="heading-footnotes">Footnotes</h3>
<p>To add a footnote reference, add a caret and an identifier inside brackets <code>([^1])</code> using the following syntax:</p>
<pre><code class="lang-markdown">Here's a simple footnote,[^1] and here's a longer one.[^bignote]

[<span class="hljs-symbol">^1</span>]: <span class="hljs-link">This is the first footnote.</span>

[<span class="hljs-symbol">^bignote</span>]: <span class="hljs-link">Here's one with multiple paragraphs and code.</span>
</code></pre>
<h3 id="heading-alerts">Alerts</h3>
<p>Alerts are a Markdown extension based on the block quote syntax that you can use to emphasize important information.</p>
<p>GitHub Flavored Markdown supports five types of alerts: <code>[!NOTE]</code>, <code>[!TIP]</code>, <code>[!IMPORTANT]</code>, <code>[!WARNING]</code>, and <code>[!CAUTION]</code>. You can use any of them:</p>
<pre><code class="lang-markdown"><span class="hljs-quote">&gt; [!NOTE]</span>
<span class="hljs-quote">&gt; Useful information that users should know, even when skimming content.</span>

<span class="hljs-quote">&gt; [!TIP]</span>
<span class="hljs-quote">&gt; Helpful advice for doing things better or more easily.</span>

<span class="hljs-quote">&gt; [!IMPORTANT]</span>
<span class="hljs-quote">&gt; Key information users need to know to achieve their goal.</span>

<span class="hljs-quote">&gt; [!WARNING]</span>
<span class="hljs-quote">&gt; Urgent info that needs immediate user attention to avoid problems.</span>

<span class="hljs-quote">&gt; [!CAUTION]</span>
<span class="hljs-quote">&gt; Advises about risks or negative outcomes of certain actions.</span>
</code></pre>
<p>The Alert syntax looks like this in the browser:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/04/alert.png" alt="Adding alert example in markdown." width="600" height="400" loading="lazy">
<em>Adding alert example in markdown.</em></p>
<h2 id="heading-advanced-formatting-syntax">Advanced Formatting Syntax</h2>
<p>This advanced formatting syntax section contains advanced use cases, such as adding diagrams and tables, collapsed sections, mathematical expressions, and more.</p>
<ol>
<li><a class="post-section-overview" href="#heading-creating-a-table">Creating a table</a></li>
<li><a class="post-section-overview" href="#heading-creating-a-collapsed-section">Creating a collapsed section</a></li>
<li><a class="post-section-overview" href="#heading-creating-diagrams">Creating diagrams</a></li>
<li><a class="post-section-overview" href="#heading-mathematical-expressions">Mathematical expressions</a></li>
</ol>
<h3 id="heading-creating-a-table">Creating a table</h3>
<p>To create tables in Markdown, you can use pipes <code>|</code> and hyphens <code>-</code>. Hyphens are used to create a column's header, while pipes are used to separate columns.</p>
<pre><code class="lang-markdown">| First Header  | Second Header |
| ------------- | ------------- |
| Content Cell  | Content Cell  |
| Content Cell  | Content Cell  |
</code></pre>
<p>The table looks like this in the browser:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/04/table-in-markdown.png" alt="Table example in markdown." width="600" height="400" loading="lazy">
<em>Table example in markdown.</em></p>
<h3 id="heading-creating-a-collapsed-section">Creating a collapsed section</h3>
<p>To create a collapsed section in a markdown file, you can use the <code>&lt;details&gt;</code> tag. This tag is an HTML element that you can easily use to extend the functionality of GitHub Flavored Markdown. Here's how it works:</p>
<pre><code class="lang-markdown"><span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">details</span>&gt;</span></span>
  <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">summary</span>&gt;</span></span>Click to here. <span class="xml"><span class="hljs-tag">&lt;/<span class="hljs-name">summary</span>&gt;</span></span>

   ### You can add a message here

   You can add text within a collapsed section. 

   You can add an image or a code block, too.

   <span class="hljs-code">```ruby
     puts "Hello World"</span>
</code></pre>
<p></p>
<pre><code>
The collapsed syntax looks like <span class="hljs-built_in">this</span> <span class="hljs-keyword">in</span> the browser:

![Collapsed example <span class="hljs-keyword">in</span> markdown.](https:<span class="hljs-comment">//www.freecodecamp.org/news/content/images/2024/04/Collapsed-in-markdown.png)</span>
_Collapsed example <span class="hljs-keyword">in</span> markdown._

### Creating diagrams

To add diagrams to a Markdown file, use triple backticks and wrap them inside quadruple backticks. Then, tell which identifier (Mermaid, GeoJSON, TopJSON, ASCII STL) you used <span class="hljs-keyword">for</span> the diagram.

GitHub supports diagrams using four syntaxes: mermaid, geoJSON, topoJSON, and ASCII STL.

<span class="hljs-number">1.</span> [Mermaid](#heading-mermaid) 
<span class="hljs-number">2.</span> [GeoJSON and TopoJSON](#heading-geojson-and-topojson)
<span class="hljs-number">3.</span> [ASCII STL](#heading-ascii-stl)

#### Mermaid 

[Mermaid](https:<span class="hljs-comment">//mermaid.js.org) is a Markdown-inspired tool that renders text into diagrams. You can create flow charts, sequence diagrams, pie charts, and more with Mermaid.</span>

The GitHub-flavored Markdown has extended the functionality <span class="hljs-keyword">of</span> using Mermaid <span class="hljs-keyword">with</span> Markdown.

You can create flow charts, sequence diagrams, pie charts, and so on inside Markdown. GitHub handles the rest <span class="hljs-keyword">of</span> that. So how <span class="hljs-keyword">do</span> you render diagrams on the screen?

<span class="hljs-string">``</span><span class="hljs-string">`markdown
`</span><span class="hljs-string">``</span>mermaid
graph LR;
   A --  and --&gt; B -- to --&gt; C
</code></pre><pre><code>
The mermaid syntax looks like <span class="hljs-built_in">this</span> <span class="hljs-keyword">in</span> the browser.

![Mermaid example <span class="hljs-keyword">in</span> markdown.](https:<span class="hljs-comment">//www.freecodecamp.org/news/content/images/2024/04/mermaid-degram.png)</span>
_Mermaid example <span class="hljs-keyword">in</span> markdown._

#### GeoJSON and TopoJSON

You can use [GeoJSON](https:<span class="hljs-comment">//geojson.org/) or [TopoJSON](https://github.com/topojson/topojson) to add an interactive map to a GitHub repository in a README file or GitHub Wiki.</span>

You can use code block syntax to add an interactive map.

<span class="hljs-number">1.</span> GeoJSON can create a map by specifying coordinates. To add an interactive map, use the following syntax:  <span class="hljs-string">` `</span><span class="hljs-string">``</span>geojson  your code <span class="hljs-string">``</span><span class="hljs-string">` `</span>
<span class="hljs-number">2.</span> TopoJSON can create a map by specifying coordinates and shapes. To add an interactive map, use the following syntax: <span class="hljs-string">` `</span><span class="hljs-string">``</span>topojson  your code <span class="hljs-string">``</span><span class="hljs-string">` `</span>

**Example using GeoJSON:**

<span class="hljs-string">``</span><span class="hljs-string">`markdown
`</span><span class="hljs-string">``</span>geojson
{
  <span class="hljs-string">"type"</span>: <span class="hljs-string">"FeatureCollection"</span>,
  <span class="hljs-string">"features"</span>: [
    {
      <span class="hljs-string">"type"</span>: <span class="hljs-string">"Feature"</span>,
      <span class="hljs-string">"id"</span>: <span class="hljs-number">1</span>,
      <span class="hljs-string">"properties"</span>: {
        <span class="hljs-string">"ID"</span>: <span class="hljs-number">0</span>
      },
      <span class="hljs-string">"geometry"</span>: {
        <span class="hljs-string">"type"</span>: <span class="hljs-string">"Polygon"</span>,
        <span class="hljs-string">"coordinates"</span>: [
          [
              [<span class="hljs-number">-90</span>,<span class="hljs-number">35</span>],
              [<span class="hljs-number">-90</span>,<span class="hljs-number">30</span>],
              [<span class="hljs-number">-85</span>,<span class="hljs-number">30</span>],
              [<span class="hljs-number">-85</span>,<span class="hljs-number">35</span>],
              [<span class="hljs-number">-90</span>,<span class="hljs-number">35</span>]
          ]
        ]
      }
    }
  ]
}
</code></pre><pre><code>
**Example <span class="hljs-keyword">of</span> TopJSON:**

<span class="hljs-string">``</span><span class="hljs-string">`markdown
`</span><span class="hljs-string">``</span>topojson
{
  <span class="hljs-string">"type"</span>: <span class="hljs-string">"Topology"</span>,
  <span class="hljs-string">"transform"</span>: {
    <span class="hljs-string">"scale"</span>: [<span class="hljs-number">0.0005000500050005</span>, <span class="hljs-number">0.00010001000100010001</span>],
    <span class="hljs-string">"translate"</span>: [<span class="hljs-number">100</span>, <span class="hljs-number">0</span>]
  },
  <span class="hljs-string">"objects"</span>: {
    <span class="hljs-string">"example"</span>: {
      <span class="hljs-string">"type"</span>: <span class="hljs-string">"GeometryCollection"</span>,
      <span class="hljs-string">"geometries"</span>: [
        {
          <span class="hljs-string">"type"</span>: <span class="hljs-string">"Point"</span>,
          <span class="hljs-string">"properties"</span>: {<span class="hljs-string">"prop0"</span>: <span class="hljs-string">"value0"</span>},
          <span class="hljs-string">"coordinates"</span>: [<span class="hljs-number">4000</span>, <span class="hljs-number">5000</span>]
        },
        {
          <span class="hljs-string">"type"</span>: <span class="hljs-string">"LineString"</span>,
          <span class="hljs-string">"properties"</span>: {<span class="hljs-string">"prop0"</span>: <span class="hljs-string">"value0"</span>, <span class="hljs-string">"prop1"</span>: <span class="hljs-number">0</span>},
          <span class="hljs-string">"arcs"</span>: [<span class="hljs-number">0</span>]
        },
        {
          <span class="hljs-string">"type"</span>: <span class="hljs-string">"Polygon"</span>,
          <span class="hljs-string">"properties"</span>: {<span class="hljs-string">"prop0"</span>: <span class="hljs-string">"value0"</span>,
            <span class="hljs-string">"prop1"</span>: {<span class="hljs-string">"this"</span>: <span class="hljs-string">"that"</span>}
          },
          <span class="hljs-string">"arcs"</span>: [[<span class="hljs-number">1</span>]]
        }
      ]
    }
  },
  <span class="hljs-string">"arcs"</span>: [[[<span class="hljs-number">4000</span>, <span class="hljs-number">0</span>], [<span class="hljs-number">1999</span>, <span class="hljs-number">9999</span>], [<span class="hljs-number">2000</span>, <span class="hljs-number">-9999</span>], [<span class="hljs-number">2000</span>, <span class="hljs-number">9999</span>]],[[<span class="hljs-number">0</span>, <span class="hljs-number">0</span>], [<span class="hljs-number">0</span>, <span class="hljs-number">9999</span>], [<span class="hljs-number">2000</span>, <span class="hljs-number">0</span>], [<span class="hljs-number">0</span>, <span class="hljs-number">-9999</span>], [<span class="hljs-number">-2000</span>, <span class="hljs-number">0</span>]]]
}
</code></pre><pre><code>
### ASCII STL

GitHub Flavored Markdown supports STL syntax. STL syntax allows you to add interactive <span class="hljs-number">3</span>D models <span class="hljs-keyword">in</span> markdown. You can use the following syntax: <span class="hljs-string">` `</span><span class="hljs-string">``</span>stl your code.<span class="hljs-string">``</span><span class="hljs-string">` `</span>

<span class="hljs-string">``</span><span class="hljs-string">`markdown
`</span><span class="hljs-string">``</span>stl
solid cube_corner
  facet normal <span class="hljs-number">0.0</span> <span class="hljs-number">-1.0</span> <span class="hljs-number">0.0</span>
    outer loop
      vertex <span class="hljs-number">0.0</span> <span class="hljs-number">0.0</span> <span class="hljs-number">0.0</span>
      vertex <span class="hljs-number">1.0</span> <span class="hljs-number">0.0</span> <span class="hljs-number">0.0</span>
      vertex <span class="hljs-number">0.0</span> <span class="hljs-number">0.0</span> <span class="hljs-number">1.0</span>
    endloop
  endfacet
  facet normal <span class="hljs-number">0.0</span> <span class="hljs-number">0.0</span> <span class="hljs-number">-1.0</span>
    outer loop
      vertex <span class="hljs-number">0.0</span> <span class="hljs-number">0.0</span> <span class="hljs-number">0.0</span>
      vertex <span class="hljs-number">0.0</span> <span class="hljs-number">1.0</span> <span class="hljs-number">0.0</span>
      vertex <span class="hljs-number">1.0</span> <span class="hljs-number">0.0</span> <span class="hljs-number">0.0</span>
    endloop
  endfacet
  facet normal <span class="hljs-number">-1.0</span> <span class="hljs-number">0.0</span> <span class="hljs-number">0.0</span>
    outer loop
      vertex <span class="hljs-number">0.0</span> <span class="hljs-number">0.0</span> <span class="hljs-number">0.0</span>
      vertex <span class="hljs-number">0.0</span> <span class="hljs-number">0.0</span> <span class="hljs-number">1.0</span>
      vertex <span class="hljs-number">0.0</span> <span class="hljs-number">1.0</span> <span class="hljs-number">0.0</span>
    endloop
  endfacet
  facet normal <span class="hljs-number">0.577</span> <span class="hljs-number">0.577</span> <span class="hljs-number">0.577</span>
    outer loop
      vertex <span class="hljs-number">1.0</span> <span class="hljs-number">0.0</span> <span class="hljs-number">0.0</span>
      vertex <span class="hljs-number">0.0</span> <span class="hljs-number">1.0</span> <span class="hljs-number">0.0</span>
      vertex <span class="hljs-number">0.0</span> <span class="hljs-number">0.0</span> <span class="hljs-number">1.0</span>
    endloop
  endfacet
endsolid
</code></pre><pre><code>
The STL syntax looks like <span class="hljs-built_in">this</span> <span class="hljs-keyword">in</span> the browser:

![STL example <span class="hljs-keyword">in</span> markdown.](https:<span class="hljs-comment">//www.freecodecamp.org/news/content/images/2024/04/stl-example.png)</span>
_STL example <span class="hljs-keyword">in</span> markdown._

### Mathematical expressions

You can add mathematical expressions, such <span class="hljs-keyword">as</span> equations, terms, formulas, and so on, to a GitHub markdown file. GitHub uses [LaTeX](https:<span class="hljs-comment">//www.cmor-faculty.rice.edu/~heinken/latex/symbols.pdf) formatted within Markdown. There are two ways to add these expressions:</span>

<span class="hljs-number">1.</span> Writing inline math expressions
<span class="hljs-number">2.</span> Writing math expressions <span class="hljs-keyword">as</span> code blocks

#### Writing inline math expressions

An inline math expression starts <span class="hljs-keyword">with</span> <span class="hljs-string">`$`</span> and ends <span class="hljs-keyword">with</span> <span class="hljs-string">`$`</span>. 

<span class="hljs-string">``</span><span class="hljs-string">`markdown
Inline math expression example: $\sqrt{3x-1}+(1+x)^2$</span>
</code></pre><p>The inline math syntax looks like this in the browser:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/04/math-exp.png" alt="Inline math expression example" width="600" height="400" loading="lazy">
<em>Inline math expression example</em></p>
<h4 id="heading-writing-math-expressions-as-code-blocks">Writing math expressions as code blocks</h4>
<p>To add a math expression's code block to the Markdown file, use the <code>```math</code> code block and wrap it inside <code>``` </code> backticks to display the expression as a block.</p>
<p>To add a math expression's code block to the Markdown file, use the ````math code block and wrap it inside triple backticks to display the expression as a block.</p>
<pre><code class="lang-markdown"><span class="hljs-code">```math
\left( \sum_{k=1}^n a_k b_k \right)^2 \leq \left( \sum_{k=1}^n a_k^2 \right) \left( \sum_{k=1}^n b_k^2 \right)</span>
</code></pre>
<p>```</p>
<p>The math code block syntax looks like this in the browser:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/04/math-expre-.png" alt="Image" width="600" height="400" loading="lazy">
<em>Code block math expression example</em></p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>Markdown syntax works well in GitHub and all other central Git servers, such as GitLab, Gitea, and so on.</p>
<p>Different tools name their markdown differently. For example, GitHub extends markdown functionality in its own way and builds GitHub Flavored Markdown. GitLab also extends markdown functionality and builds and creates a GitLab-flavored markdown.</p>
<p>Markdown syntax is mostly the same in every Git service. But alerts, diagrams, and a few other features only work in GitHub Flavored Markdown.</p>
<h2 id="heading-reference">Reference</h2>
<ul>
<li>GitHub docs – <a target="_blank" href="https://docs.github.com/en/get-started/writing-on-github/getting-started-with-writing-and-formatting-on-github/quickstart-for-writing-on-github">quickstart for writing on GitHub</a></li>
<li>GitHub docs  – <a target="_blank" href="https://docs.github.com/en/get-started/writing-on-github/getting-started-with-writing-and-formatting-on-github/basic-writing-and-formatting-syntax">basic syntax</a></li>
<li>Tutorial about <a target="_blank" href="https://www.vanderveer.io/github-markdown-render-stl/">rendering STL in Markdown on GitHub</a></li>
</ul>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Install Neovim Using the nvchad Framework ]]>
                </title>
                <description>
                    <![CDATA[ Neovim is a popular IDE and is a solid alternative to VS Code. Neovim supports every major programming language and allows you to build anything, anywhere.  Neovim can be a bit problematic to start with, especially for newcomers. Writing a Neovim con... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/install-neovim-with-nvchad/</link>
                <guid isPermaLink="false">66d038bbdac44f94fd94e9d6</guid>
                
                    <category>
                        <![CDATA[ ide ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Integrated Development Environment   ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Rajdeep Singh ]]>
                </dc:creator>
                <pubDate>Thu, 14 Mar 2024 14:17:07 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2024/03/How-to-Install-Neovim-Using-the-nvchad-Framework.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p><a target="_blank" href="https://neovim.io/">Neovim</a> is a popular IDE and is a solid alternative to VS Code. Neovim supports every major programming language and allows you to build anything, anywhere. </p>
<p>Neovim can be a bit problematic to start with, especially for newcomers. Writing a Neovim configuration from scratch is often difficult. To resolve this issue, we will install Neovim using the Nvchad framework. </p>
<p><a target="_blank" href="https://nvchad.com/">Nvchad</a> is a Neovim framework/configuration provider that has a rich, beautiful UI interface, blazing-fast startup time, and helps you work productively with Neovim.</p>
<p>You don't need to configure everything from scratch, as most things come pre-configured. There are multiple themes, code snippets, syntax highlighting, LSP configuration, plugin management, key mapping, and other helpful features.</p>
<p>In this article, I'll give you step-by-step instructions on installing Neovim and nvchad from scratch in your Linux and Debian based distro.</p>
<h2 id="heading-how-to-set-up-the-project">How to Set Up the Project</h2>
<p>To download Neovim and nvchad in your distro, you'll need some additional command line tools. These will help you install the software:</p>
<ol>
<li><a class="post-section-overview" href="#heading-install-the-git-cli">Git CLI</a></li>
<li><a class="post-section-overview" href="#heading-install-the-curl-cli">Curl CLI</a></li>
<li><a class="post-section-overview" href="#heading-install-the-unzip-cli">Unzip CLI</a></li>
<li><a class="post-section-overview" href="#heading-install-the-fc-cache-font-config-cli">Fc cache (Font Config) CLI</a></li>
</ol>
<p>Let's go through installing these tools to make sure you have them:</p>
<h3 id="heading-install-the-git-cli">Install the Git CLI</h3>
<p>To install Git, run the following command:</p>
<pre><code class="lang-bash">sudo apt-get install git
</code></pre>
<h3 id="heading-install-the-curl-cli">Install the Curl CLI</h3>
<p>To install curl, run the following command:</p>
<pre><code class="lang-bash">sudo apt-get install curl
</code></pre>
<h3 id="heading-install-the-unzip-cli">Install the Unzip CLI</h3>
<p>To install Unzip, run the following command:</p>
<pre><code class="lang-bash">sudo apt-get install unzip
</code></pre>
<h3 id="heading-install-the-fc-cache-font-config-cli">Install the Fc cache (Font Config) CLI</h3>
<p>To install the Fc cache CLI, run the following command.</p>
<pre><code class="lang-bash">sudo apt install fontconfig
</code></pre>
<h2 id="heading-how-to-install-neovim-and-nvchad">How to Install Neovim and nvchad</h2>
<p>If you follow these steps, you can easily install Neovim and nvchad, even if you are a newcomer. It takes some time, but you don't need to have deep knowledge about Neovim and nvchad to get them set up.</p>
<h3 id="heading-install-neovim">Install Neovim</h3>
<p>The first step is to install Neovim on your machine. To do that, you'll need to run the following command depending on your distro:</p>
<pre><code class="lang-bash"><span class="hljs-comment"># Ubuntu ( Snap User)</span>
sudo snap install nvim --classic

<span class="hljs-comment"># NixOS</span>
nix-env -iA nixpkgs.neovim

<span class="hljs-comment"># MacOS</span>
brew install neovim

<span class="hljs-comment"># Arch Linux</span>
sudo pacman -S neovim
</code></pre>
<p>For other operating systems such as Windows, you can read the <a target="_blank" href="https://github.com/neovim/neovim/blob/master/INSTALL.md">installation documentation Page</a>. I've also written an article on <a target="_blank" href="https://medium.com/thelinux/the-correct-way-to-install-the-neovim-42f3076f9b88"><strong>the correct way to install Neovim</strong></a>, which you can also check out.</p>
<h2 id="heading-how-to-install-nerd-font">How to Install Nerd Font</h2>
<p>The next step is to install Nerd Font on your laptop or operating system. Nerd Font is a prerequisite for nvchad. If you cannot download Nerd Font, your nvchad UI will not work.</p>
<p>To install Nerd Font in Debian or Debian-based distros and macOS, you can run the following command:</p>
<pre><code class="lang-bash"><span class="hljs-comment"># Debain, Ubuntu, Linux Mint, Kali Linux, etc.</span>
bash -c  <span class="hljs-string">"<span class="hljs-subst">$(curl -fsSL https://raw.githubusercontent.com/officialrajdeepsingh/nerd-fonts-installer/main/install.sh)</span>"</span> 

<span class="hljs-comment"># MacOS</span>
brew tap homebrew/cask-fonts &amp;&amp; brew install --cask font-&lt;Nerd-FONT- NAME&gt;-nerd-font
</code></pre>
<p>Before running the <a target="_blank" href="https://github.com/officialrajdeepsingh/nerd-fonts-installer">nerd-fonts-installer</a>, make sure you've installed <strong>curl</strong>, <strong>unzip</strong>, and <strong>Fc cache</strong> CLI in your Debian distro, following the instructions above.</p>
<h2 id="heading-how-to-install-nvchad">How to Install nvchad</h2>
<p>The last and final step is to install the nvchad framework in Neovim. To do so, run the following command:</p>
<pre><code class="lang-bash"><span class="hljs-comment"># Linux &amp; macOS</span>
git <span class="hljs-built_in">clone</span> https://github.com/NvChad/starter ~/.config/nvim &amp;&amp; nvim
</code></pre>
<p>The following command takes some time, depending on your internet speed, and installs additional plugins required by nvchad from the internet. </p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>For a newcomer starting with Neovim, the nvchad framework is a great choice. Without nvchad, configuring Neovim from scratch is a hard task for a beginner. Choosing the Neovim framework (configuration) is the right choice for newcomers.</p>
<p>Before starting with Neovim, read up and make sure It is the right choice for you. I recently found a <a target="_blank" href="https://marketplace.visualstudio.com/items?itemName=asvetliakov.vscode-neovim">VS Code plugin</a> created and maintained by Neovim. You can get the same Neovim experience inside VS Code. After that, you can decide which you prefer.</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Run GitHub Actions Locally Using the act CLI Tool ]]>
                </title>
                <description>
                    <![CDATA[ GitHub Actions help automate tasks like building, testing, and deploying in your GitHub repository.  With one click, you can publish your production-ready code or package on npm, GitHub pages, docker images, deploy your production code on a cloud pro... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-run-github-actions-locally/</link>
                <guid isPermaLink="false">66d038b241966f84606807ec</guid>
                
                    <category>
                        <![CDATA[ GitHub ]]>
                    </category>
                
                    <category>
                        <![CDATA[ GitHub Actions ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Rajdeep Singh ]]>
                </dc:creator>
                <pubDate>Mon, 11 Mar 2024 20:21:13 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2024/02/How-to-run-GitHub-actions-locally.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>GitHub Actions help automate tasks like building, testing, and deploying in your GitHub repository. </p>
<p>With one click, you can publish your production-ready code or package on npm, GitHub pages, docker images, deploy your production code on a cloud provider, and so on.</p>
<p>The problem starts when you're testing GitHub Actions. It can be time-consuming and painful. First, you have to change the GitHub Actions file locally, push your local code into your GitHub repository, and wait for the result. </p>
<p>To solve this issue, You can use <a target="_blank" href="https://github.com/nektos/act">the <code>act</code></a> CLI tool to test and write the GitHub action locally. With <code>act</code> CLI, you do not need to commit/push your local code to the GitHub Repository. You test GitHub action locally on your laptop or machine.</p>
<p><strong>Here are the steps involved:</strong></p>
<ul>
<li><a class="post-section-overview" href="#heading-how-to-install-act-for-github-actions">How to install <code>act</code>.</a></li>
<li><a class="post-section-overview" href="#heading-how-to-configure-and-initialize-the-act-cli">How to configure and initialize the <code>act</code> CLI.</a></li>
<li><a class="post-section-overview" href="#how-to-use-the-act-cli-tool-">How to use the <code>act</code> CLI tool.</a></li>
</ul>
<h2 id="heading-how-to-install-act-for-github-actions">How to Install <code>act</code> for GitHub Actions</h2>
<p>The <a target="_blank" href="https://github.com/nektos/act"><code>act</code> CLI</a> tool works with Docker. Before starting with <code>act</code> CLI, First, <a target="_blank" href="https://www.docker.com/">install Docker</a> on your system or laptop.</p>
<p>To install the <code>act</code> CLI, you need to run the following command:</p>
<pre><code class="lang-bash"><span class="hljs-comment"># Window</span>
choco install act-cli

<span class="hljs-comment"># MacOS</span>
brew install act

<span class="hljs-comment"># Linux</span>
curl https://raw.githubusercontent.com/nektos/act/master/install.sh | sudo bash
</code></pre>
<h2 id="heading-how-to-configure-and-initialize-the-act-cli">How to Configure and Initialize the <code>act</code> CLI</h2>
<p>After the <code>act</code> CLI installation is successful on your laptop or machine, the next step is to run it in your project.</p>
<p><code>act</code> CLI asks which Docker image size—large, medium, or micro—must be installed during installation.</p>
<p>There are various Docker image sizes:</p>
<ol>
<li>The Docker Micro image size is 200 MB, and small projects use it.</li>
<li>The Docker Medium image size is 500 MB, and Big Project uses it.</li>
<li>The Docker Large image size is 17 GB, and Enterprise uses it.</li>
</ol>
<p>The <code>act</code> CLI uses the Docker image to run the GitHub action locally. </p>
<pre><code class="lang-bash">$ act
</code></pre>
<p>The command output in the terminal looks like this:</p>
<pre><code class="lang-bash">$ test-github-actions git:(main) ✗ act
? Please choose the default image you want to use with act:
  - Large size image: ca. 17GB download + 53.1GB storage, you will need 75GB of free disk space, snapshots of GitHub Hosted Runners without snap and pulled docker images
  - Medium size image: ~500MB, includes only necessary tools to bootstrap actions and aims to be compatible with most actions
  - Micro size image: &lt;200MB, contains only NodeJS required to bootstrap actions, doesn<span class="hljs-string">'t work with all actions

Default image and other options can be changed manually in ~/.actrc (please refer to https://github.com/nektos/act#configuration for additional information about file structure) Micro
[Build Ghost and test theme/install] 🚀  Start image=node:16-buster-slim
INFO[0023] Parallel tasks (0) below minimum, setting to 1 
[Build Ghost and test theme/install]   🐳  docker pull image=node:16-buster-slim platform= username= forcePull=true
INFO[0031] Parallel tasks (0) below minimum, setting to 1 
[Build Ghost and test theme/install]   🐳  docker create image=node:16-buster-slim platform= entrypoint=["tail" "-f" "/dev/null"] cmd=[] network="host"
[Build Ghost and test theme/install]   🐳  docker run image=node:16-buster-slim platform= entrypoint=["tail" "-f" "/dev/null"] cmd=[] network="host"
[Build Ghost and test theme/install]   ☁  git clone '</span>https://github.com/vimtor/action-zip<span class="hljs-string">' # ref=v1.2
[Build Ghost and test theme/install]   ☁  git clone '</span>https://github.com/softprops/action-gh-release<span class="hljs-string">' # ref=v0.1.15
[Build Ghost and test theme/install] ⭐ Run Main actions/checkout@v4
[Build Ghost and test theme/install]   🐳  docker cp src=/home/officialrajdeepsingh/medium/test-github-actions/. dst=/home/officialrajdeepsingh/medium/test-github-actions
[Build Ghost and test theme/install]   ✅  Success - Main actions/checkout@v4
[Build Ghost and test theme/install] ⭐ Run Main Easy Zip Files
[Build Ghost and test theme/install]   🐳  docker cp src=/home/officialrajdeepsingh/.cache/act/vimtor-action-zip@v1.2/ dst=/var/run/act/actions/vimtor-action-zip@v1.2/
[Build Ghost and test theme/install]   🐳  docker exec cmd=[node /var/run/act/actions/vimtor-action-zip@v1.2/dist/index.js] user= workdir=
| Ready to zip "build/ home.txt" into example.zip
|   - build/
|   - home.txt
| 
| Zipped file example.zip successfully
[Build Ghost and test theme/install]   ✅  Success - Main Easy Zip Files
[Build Ghost and test theme/install] Cleaning up container for job install
[Build Ghost and test theme/install] 🏁  Job succeeded</span>
</code></pre>
<p>After downloading the image from the Docker repository, the <code>act</code> CLI runs the GitHub action.</p>
<p><code>act</code> CLI generates the <code>~/.actrc</code> file in the laptop for configuration. The <code>~/.actrc</code> file contains the Docker image name. </p>
<pre><code class="lang-bash"><span class="hljs-comment"># .actrc</span>
-P ubuntu-latest=node:16-buster-slim
-P ubuntu-22.04=node:16-bullseye-slim
-P ubuntu-20.04=node:16-buster-slim
-P ubuntu-18.04=node:16-buster-slim
</code></pre>
<p>To install other Docker images, remove the <code>~/.actrc</code> file and re-run the <code>act</code> CLI to install the different Docker images.</p>
<h3 id="heading-error">Error</h3>
<p>Due to dependence on Docker, we may face some errors when initializing an <code>act</code> CLI for the first time. </p>
<pre><code class="lang-bash">$ act
</code></pre>
<p>The error should look like this:</p>
<pre><code class="lang-bash">$ test-github-actions git:(main) ✗ act       
ERRO[0000] daemon Docker Engine socket not found and containerDaemonSocket option was not <span class="hljs-built_in">set</span> 
? Please choose the default image you want to use with act:
  - Large size image: ca. 17GB download + 53.1GB storage, you will need 75GB of free disk space, snapshots of GitHub Hosted Runners without snap and pulled docker images
  - Medium size image: ~500MB, includes only necessary tools to bootstrap actions and aims to be compatible with most actions
  - Micro size image: &lt;200MB, contains only NodeJS required to bootstrap actions, doesn<span class="hljs-string">'t work with all actions

Default image and other options can be changed manually in ~/.actrc (please refer to https://github.com/nektos/act#configuration for additional information about file structure) Micro
[Build Ghost and test theme/install] 🚀  Start image=node:16-buster-slim
INFO[0305] Parallel tasks (0) below minimum, setting to 1 
[Build Ghost and test theme/install]   🐳  docker pull image=node:16-buster-slim platform= username= forcePull=true
Error: Cannot connect to the Docker daemon at unix:///var/run/docker.sock. Is the docker daemon running?</span>
</code></pre>
<p>You're seeing the <code>Cannot connect to the Docker daemon</code> error in the above code. This issue occurs due to the Docker daemon. In simple words, the Docker daemon is not running. You can resolve this issue by starting your Docker and re-running your <code>act</code> command.</p>
<p>There are two ways to run Docker services.</p>
<ol>
<li>Open Docker desktop in your window, and your Docker service is started.</li>
<li>Run Docker with the <code>systemctl start docker</code> command on Linux.</li>
</ol>
<p>You can verify whether your Docker is running or not with the following command:</p>
<pre><code class="lang-bash">$ systemctl status docker

● docker.service - Docker Application Container Engine
     Loaded: loaded (/etc/systemd/system/docker.service; enabled; preset: enabled)
    Drop-In: /nix/store/fibzdkfv6in4xw39rm0c7bq4nadzisas-system-units/docker.service.d
             └─overrides.conf
     Active: active (running) since Mon 2024-02-26 12:38:37 IST; 3h 39min ago
TriggeredBy: ● docker.socket
       Docs: https://docs.docker.com
   Main PID: 1186 (dockerd)
         IP: 0B <span class="hljs-keyword">in</span>, 0B out
         IO: 109.0M <span class="hljs-built_in">read</span>, 152.0K written
      Tasks: 40
     Memory: 148.5M
        CPU: 1min 40.817s
     CGroup: /system.slice/docker.service
             ├─1186 /nix/store/7pzis8dkhs461kl1bg2fp0202dw6r5i5-moby-24.0.5/libexec/docker/dockerd --config-file=/nix/store/3rlv5f0zldcc120b01szywidl0qz9x4p-daemon.json
             └─1256 containerd --config /var/run/docker/containerd/containerd.toml

Feb 26 12:38:37 nixos dockerd[1256]: time=<span class="hljs-string">"2024-02-26T12:38:37.532987858+05:30"</span> level=info msg=<span class="hljs-string">"containerd successfully booted in 0.016901s"</span>
Feb 26 12:38:37 nixos dockerd[1186]: time=<span class="hljs-string">"2024-02-26T12:38:37.562515048+05:30"</span> level=info msg=<span class="hljs-string">"[graphdriver] using prior storage driver: overlay2"</span>
Feb 26 12:38:37 nixos dockerd[1186]: time=<span class="hljs-string">"2024-02-26T12:38:37.564062690+05:30"</span> level=info msg=<span class="hljs-string">"Loading containers: start."</span>
Feb 26 12:38:37 nixos dockerd[1186]: time=<span class="hljs-string">"2024-02-26T12:38:37.778478313+05:30"</span> level=info msg=<span class="hljs-string">"Default bridge (docker0) is assigned with an IP address 172.17.0.0/16. Daemon option --bip can be used to set a preferred IP address"</span>
Feb 26 12:38:37 nixos dockerd[1186]: time=<span class="hljs-string">"2024-02-26T12:38:37.805931545+05:30"</span> level=info msg=<span class="hljs-string">"Loading containers: done."</span>
Feb 26 12:38:37 nixos dockerd[1186]: time=<span class="hljs-string">"2024-02-26T12:38:37.828589904+05:30"</span> level=info msg=<span class="hljs-string">"Docker daemon"</span> commit=v24.0.5 graphdriver=overlay2 version=24.0.5
Feb 26 12:38:37 nixos dockerd[1186]: time=<span class="hljs-string">"2024-02-26T12:38:37.828929197+05:30"</span> level=info msg=<span class="hljs-string">"Daemon has completed initialization"</span>
Feb 26 12:38:37 nixos systemd[1]: Started Docker Application Container Engine.
Feb 26 12:38:37 nixos dockerd[1186]: time=<span class="hljs-string">"2024-02-26T12:38:37.841992729+05:30"</span> level=info msg=<span class="hljs-string">"API listen on /run/docker.sock"</span>
Feb 26 12:38:37 nixos dockerd[1186]: time=<span class="hljs-string">"2024-02-26T12:38:37.841993669+05:30"</span> level=info msg=<span class="hljs-string">"API listen on /run/docker.sock"</span>
</code></pre>
<h2 id="heading-how-to-use-the-act-cli-tool">How to Use the <code>act</code> CLI Tool</h2>
<p><code>act</code> CLI has many options, but we'll look at some important ones. You can check all the options by running the <code>act --help</code> command.</p>
<h3 id="heading-act-cli-options"><code>act</code> CLI Options</h3>
<p>Here are some <code>act</code> CLI options:</p>
<ul>
<li><a class="post-section-overview" href="#heading-events">Events</a></li>
<li><a class="post-section-overview" href="#heading-lists">Lists</a></li>
<li><a class="post-section-overview" href="#heading-running-specific-jobs">Running Specific Jobs</a></li>
<li><a class="post-section-overview" href="#heading-graph">Graph</a></li>
<li><a class="post-section-overview" href="#heading-environment-variables">Environment Variables</a></li>
<li><a class="post-section-overview" href="#heading-secrets">Secrets</a></li>
</ul>
<h3 id="heading-events">Events</h3>
<p><code>act</code> CLI's default action is the push action, which triggers only push events by default.</p>
<pre><code class="lang-bash">$ act
</code></pre>
<p>You can change the event after passing the second argument, which is the name of your action. In our case, we'll pass pull_request.</p>
<pre><code class="lang-bash">$ act pull_request
</code></pre>
<p>There is a long list available to trigger workflows. You can <a target="_blank" href="https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows">read it on the GitHub action</a> documentation.</p>
<h3 id="heading-lists">Lists</h3>
<p>The list option prints all the available jobs that you write in <code>.github/workflows</code>.</p>
<pre><code class="lang-bash">$ act -l
</code></pre>
<p>The command output in the terminal looks like this.</p>
<pre><code class="lang-bash">$ act -l    
Stage  Job ID             Job name           Workflow name                  Workflow file      Events      
0      zip                zip                Convert files into Zip         build-project.yml  release     
0      request_test       request_test       Pull Request                   fork.yml           fork        
0      pull_request_test  pull_request_test  Pull Request                   issues.yml         issues      
0      show               show               Convert files into Zip folder  test.yml           pull_request
</code></pre>
<h3 id="heading-running-specific-jobs">Running Specific Jobs</h3>
<p>You use the <code>--job</code> option command to run specific jobs from your workflows. </p>
<p>Make sure your job name is unique – otherwise, it runs all jobs containing the same in your workflow. Whenever you can not pass an event by default, trigger the push event.</p>
<h3 id="heading-syntax">Syntax</h3>
<pre><code class="lang-bash">act --job &lt;name-of-your-job&gt;
</code></pre>
<p>For example, we run a specific show job.</p>
<pre><code class="lang-bash">$ act --job <span class="hljs-string">'show'</span>
</code></pre>
<h3 id="heading-graph">Graph</h3>
<p>The graph option draws the available workflow jobs structure in your terminal as a graph.</p>
<pre><code class="lang-bash">$ act --graph
</code></pre>
<p>The command output in the terminal looks like this:</p>
<pre><code class="lang-bash">$ act --graph
 ╭─────╮ ╭──────────────╮ ╭───────────────────╮ ╭──────╮
 │ zip │ │ request_test │ │ pull_request_test │ │ show │
 ╰─────╯ ╰──────────────╯ ╰───────────────────╯ ╰──────╯
</code></pre>
<h3 id="heading-environment-variables">Environment Variables</h3>
<p>Using environment variables with the <code>act</code> CLI is easy. You only need to create a new <code>.env</code> file. <code>act</code> CLI automatically loads the environment that is available in the <code>.env</code> file. For example, we add a <code>ENV_ID</code> variables.</p>
<pre><code class="lang-yaml"><span class="hljs-comment"># .env</span>

<span class="hljs-string">ENV_ID='My</span> <span class="hljs-string">Env'</span>
</code></pre>
<p>To use the <code>ENV_ID</code> environment variables, use the following syntax <code>${{ env.ENV_ID }}</code> in your GitHub action:</p>
<pre><code class="lang-bash"><span class="hljs-comment"># .github/workflows/test.yml</span>

name: Convert files into Zip folder

on: pull_request

<span class="hljs-built_in">jobs</span>:
  show:
    runs-on: ubuntu-latest
    steps:
      - name: Show Env
        run: <span class="hljs-built_in">echo</span> <span class="hljs-string">"Env <span class="hljs-variable">${{ env.ENV_ID }</span>}"</span>
</code></pre>
<p>With the <code>--env-file</code>  option, you can change the default <code>.env</code> file name to <code>my-custom.env</code> file.</p>
<pre><code class="lang-bash">$ act --env-file=my-custom.env
</code></pre>
<h3 id="heading-secrets">Secrets</h3>
<p>You must create a new <code>.secrets</code>  file to load the environment secrets with the <code>act</code> CLI. This automatically loads the environment secrets that are available in the <code>secrets</code> file. For example, we add a <code>APP_SECRET</code> and <code>APP_ID</code> variables.</p>
<pre><code class="lang-yaml"><span class="hljs-string">APP_SECRET='7824jurd789gyu45esxgfgf48822166974gtredsyujn'</span>
<span class="hljs-string">APP_ID='7878974561587'</span>
</code></pre>
<p>To use the  <code>APP_SECRET</code> environment variables, use the following syntax <code>${{ secrets.APP_SECRE}}</code> in your GitHub action:</p>
<pre><code class="lang-yaml"><span class="hljs-comment"># .github/workflows/test.yml</span>

<span class="hljs-attr">name:</span> <span class="hljs-string">Learn</span> <span class="hljs-string">environment</span> <span class="hljs-string">secrets</span> 

<span class="hljs-attr">on:</span> <span class="hljs-string">pull_request</span>

<span class="hljs-attr">jobs:</span>
  <span class="hljs-attr">show:</span>
    <span class="hljs-attr">runs-on:</span> <span class="hljs-string">ubuntu-latest</span>
    <span class="hljs-attr">steps:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Show</span> <span class="hljs-string">env</span>
        <span class="hljs-attr">run:</span> <span class="hljs-string">echo</span> <span class="hljs-string">"App SECRET $<span class="hljs-template-variable">{{ secrets.APP_SECRET }}</span>"</span>
      <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Show</span> <span class="hljs-string">varibale</span>
        <span class="hljs-attr">run:</span> <span class="hljs-string">echo</span> <span class="hljs-string">"App ID $<span class="hljs-template-variable">{{ secrets.APP_ID }}</span>"</span>
</code></pre>
<p>You can load your custom  <code>my-custom.secrets</code> file containing all your secrets with the <code>--secret-file</code>  option.</p>
<pre><code class="lang-bash">$ act --secret-file=my-custom.secrets
</code></pre>
<h2 id="heading-conclusion">Conclusion</h2>
<p><code>act</code> CLI helps save time and energy when working and testing GitHub locally. Currently, there is no alternative to <code>act</code> CLI, which allows us to run GitHub actions locally.</p>
<p><code>act</code> CLI isn't fully compatible with GitHub actions. Some features are not implemented, for example, concurrency, no <code>vars</code> context, incomplete <code>github</code> context, and so on.</p>
<p>You can hire me as a freelance developer with <a target="_blank" href="https://www.upwork.com/freelancers/~01a4e8ba7a41795229">Upwork</a> and other updates. Follow me on <a target="_blank" href="https://twitter.com/Official_R_deep">Twitter (X)</a> and <a target="_blank" href="https://officialrajdeepsingh.medium.com/">Medium</a>.</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ Documentation Libraries to Help You Write Good Docs ]]>
                </title>
                <description>
                    <![CDATA[ Good project documentation is key to success for every company, startup, or individual project. Without documentation, it's much harder for new developers or others to use your project. In this article, I'll discuss some of my favourite libraries you... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/documentation-libraries-to-help-you-write-good-docs/</link>
                <guid isPermaLink="false">66d038a215ea3036a953996a</guid>
                
                    <category>
                        <![CDATA[ documentation ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Libraries ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Rajdeep Singh ]]>
                </dc:creator>
                <pubDate>Thu, 01 Feb 2024 15:45:42 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2024/01/Documentation-Libraries-to-Help-You-Write-Good-Docs.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Good project documentation is key to success for every company, startup, or individual project. Without documentation, it's much harder for new developers or others to use your project.</p>
<p>In this article, I'll discuss some of my favourite libraries you can use for building your documentation site. </p>
<p>And don't worry if you don't have that much experience creating documentation yet. Whether you've built a simple documentation site for a small startup or personal project or a vast and complex site for a large company, these libraries will be helpful to you.</p>
<h2 id="heading-tips-for-writing-good-documentation">Tips for Writing Good Documentation</h2>
<p>Before we get into the libraries themselves, though, there are some critical points you'll want to keep in mind when you're building your documentation sites.</p>
<h3 id="heading-make-sure-your-documentation-is-clean-and-easily-recognizable">Make sure your documentation is clean and easily recognizable.</h3>
<p>Ensure your documentation folder is separate in the mono repo, even if you use a <a target="_blank" href="https://www.accenture.com/us-en/blogs/software-engineering-blog/how-to-choose-between-mono-repo-and-poly-repo">poly repo</a> or separate repository.</p>
<p>This separate repository should contain only the markdown and mdx files. This will help your contributors easily be able to recognize which folder is for documentation.</p>
<p>A great example of clean documentation is <a target="_blank" href="https://github.com/vercel/next.js">Next.js</a>, which has a separate folder for documentation, as you can see below:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/01/nextjs-documentation.png" alt="Nextjs documentation is easy to recognize in mono repo, and it contains only the markdown." width="600" height="400" loading="lazy">
<em>Nextjs documentation is <strong>easily recognizable</strong> in the mono repo and contains only the Markdown.</em></p>
<h3 id="heading-provide-clear-guidelines-in-your-documentation">Provide clear guidelines in your documentation.</h3>
<p>To improve documentation quality, you should write clear guidelines for technical writers. For example,</p>
<ol>
<li>what front matter is required in the file markdown?</li>
<li>Which spelling conventions are correct – for example, do you accept javascript (all lowercase) or JavaScript (with the proper casing)? Or both?</li>
<li>Which commands are needed for formatting and linting before applying a pull request?</li>
</ol>
<p>A pro tip for documentation sites is mentioning additional resources, such as tutorials, courses, and article links for new contributors.</p>
<p>The best examples of clear guidelines are <a target="_blank" href="https://nextjs.org/docs/community/contribution-guide">Next.js</a> and the <a target="_blank" href="https://github.com/sindresorhus/awesome/blob/main/pull_request_template.md">Awesome</a> Repository. Both have clear guidelines for documentation.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/01/docs-guidelines.png" alt="Nextjs has clear guidelines for documentation." width="600" height="400" loading="lazy">
<em>Nextjs has clear guidelines for documentation.</em></p>
<h3 id="heading-make-it-easy-to-contribute">Make it easy to contribute.</h3>
<p>When contributors want to help out with your project, many of them just want to focus on writing. Most technical writers or documentation contributors do not have time to install and set up your project locally.</p>
<p>Many online IDEs are available these days, and more are coming, such as GitHub Dev, VS Code Dev, Code Sandbox, and GitLab.</p>
<p>Nowadays, many developers and contributors update the documentation file using GitHub's inbuilt IDE or other online IDEs and create pull requests without installing your repository locally.</p>
<p>So, you should at least configure your project to work with one of the online IDEs. It helps to save time and improves the productivity of the technical writer and contributor.</p>
<h2 id="heading-helpful-documentation-libraries">Helpful Documentation Libraries:</h2>
<ol>
<li><a class="post-section-overview" href="#heading-nextra">Nextra</a></li>
<li><a class="post-section-overview" href="#heading-docusaurus">Docusaurus</a></li>
<li><a class="post-section-overview" href="#heading-lume">Lume</a></li>
<li><a class="post-section-overview" href="#docsifyjs">Docsify.js</a></li>
<li><a class="post-section-overview" href="#heading-markdoc">Markdoc</a></li>
<li><a class="post-section-overview" href="#content-layer">Content layer</a></li>
<li><a class="post-section-overview" href="#git-book">Git book</a></li>
<li><a class="post-section-overview" href="#outstatic-CMS">Outstatic CMS</a></li>
<li><a class="post-section-overview" href="#heading-code-doc">Code doc</a></li>
<li><a class="post-section-overview" href="#heading-front-matter">Frontmatter</a></li>
</ol>
<h2 id="heading-nextra">Nextra</h2>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/01/nextra.png" alt="Nextra" width="600" height="400" loading="lazy">
<em>Nextra</em></p>
<p><a target="_blank" href="https://nextra.site"><strong>Nextra</strong></a> is an open-source, simple, powerful, and flexible site generation framework built on Nextjs. Nextra was created by developers at <a target="_blank" href="https://vercel.com/">Vercel</a>.</p>
<p>Nextra helps manage your content with MDX. With Nextra, you can build both small and large-scale documentation websites.</p>
<p>Nextra comes with various built-in features, such as:</p>
<ol>
<li>Organizing content with file-system routing.</li>
<li>Theme Toggling (Light to Dark theme)</li>
<li>Inbuilt search</li>
<li>Multiple layouts</li>
<li>Syntax highlighting</li>
<li>Multiple languages (Internationalized) </li>
<li>Custom themes</li>
</ol>
<p>Nextra also helps to save you time and energy, as you can directly work on your documentation without writing a single line of code. You also do not have to maintain the code base. This lets you focus on documentation writing.</p>
<h3 id="heading-cons">Cons</h3>
<ol>
<li>There are fewer customizations you can make with Nextra</li>
<li>Nextra comes with more limited features</li>
</ol>
<p>To learn more about Nextra, <a target="_blank" href="https://medium.com/frontendweb/how-to-create-a-markdown-blog-with-nextjs-and-nextra-2985362f9708">you can check out the tutorial I wrote about it</a>. </p>
<h2 id="heading-docusaurus">Docusaurus</h2>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/01/docusaurus.png" alt="docusaurus" width="600" height="400" loading="lazy">
<em>docusaurus</em></p>
<p><strong><a target="_blank" href="https://docusaurus.io">Docusaurus</a></strong> is an open-source static-site generator built and maintained by the Meta team. Docusaurus helps you write and manage your large and small documentation and blog websites.</p>
<p>Docusaurus comes with various built-in features, such as:</p>
<ol>
<li>It's easy to configure</li>
<li>You can customize your site's layout, design, and features with React components</li>
<li>You can write content in Markdown and MDX. </li>
<li>It handles localization well</li>
<li>You can manage versioning</li>
<li>It has a built-in plugins ecosystem </li>
<li>You can customize and change themes</li>
<li>There's good client API support</li>
<li>It has TypeScript and JSDoc support</li>
<li>You can create both a blog and a documentation website with Docusaurus.</li>
</ol>
<p>Docusaurus is a well-established library used by many companies. And one of the best parts about Docusaurus is that it has a more significant number of active contributors, so the tool is well-maintained. </p>
<h3 id="heading-cons-1">Cons</h3>
<ol>
<li>Customizing and managing a large documentation website with Docusaurus can be tricky because of complex Docusaurus configuration options.</li>
<li>Configuring Docusaurus blog plugins can be a massive headache because of the configuration. Lastly, Docusaurus can not support categories for articles.</li>
<li>Docusaurus does not come with search functionality. To enable search functionality, you have to depend on a third-party service. Confirming the third-party search functionality is sometimes not an easy task.</li>
</ol>
<h2 id="heading-lume">Lume</h2>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/01/lume.png" alt="lume" width="600" height="400" loading="lazy">
<em>lume</em></p>
<p><a target="_blank" href="https://lume.land/"><strong>Lume</strong></a> is a fast and flexible open-source static site generator based on Deno. You can build documentation sites, a portfolio, a company website, or a blog with Lume. </p>
<p>Lume comes with various built-in features, such as:</p>
<ol>
<li>Processors</li>
<li>Plugins support</li>
<li>Multiple file formats, like <code>markdown</code>, <code>yaml</code>, <code>JavaScript</code>, <code>typescript</code>, <code>jsx</code> and <code>nunjucks</code>, and it's easy to extend with other features.</li>
<li>Inbuilt search and pagination support</li>
<li>It supports multiple template engines (JSX, Preact, MDX, Remark, and so on)</li>
<li>Ability to create relationships between two pages</li>
</ol>
<p>You can customize so many things with Lume that you may not even be able to imagine until you try it out. </p>
<h3 id="heading-cons-2">Cons</h3>
<ol>
<li>Starting with Lume is not an easy task. It's a steeper learning curve, and it may take some time to get enough experience to use it effectively – so Lume is not the best for beginners. </li>
<li>You need a third-party service to enable search functionality on your website.</li>
<li>Since there's so much customization possible, sometimes you might get confused about what you've chosen.</li>
</ol>
<p>Still, Lume gives you more control and power over your documentation site, so if you're willing to take the time to learn it, I think you'll enjoy it. You can <a target="_blank" href="https://www.freecodecamp.org/news/how-to-create-a-static-blog-with-lume/">read my in-depth tutorial on freeCodeCamp to learn more about Lume</a>.</p>
<h2 id="heading-docsifyjs">Docsify.js</h2>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/01/docsify.png" alt="Docsify.js" width="600" height="400" loading="lazy">
<em>Docsify.js</em></p>
<p><a target="_blank" href="https://docsify.js.org"><strong>Docsify</strong></a> is an open-source, simple, and lightweight documentation generator. It's heaven for developers with a C, Rust, and C++ background. You can start using Docsify without having any knowledge of JavaScript or React.js. </p>
<p>Docsify comes with a number of built-in features, such as:</p>
<ol>
<li>It's simple and lightweight</li>
<li>It's easy to customize and configure</li>
<li>You can extend Docsify's functionally with built-in plugin API</li>
<li>It has multiple themes support</li>
<li>There's emoji support</li>
<li>It supports server-side rendering</li>
</ol>
<p>In Docsify, you can focus on writing documentation without worrying about maintaining the codebase. </p>
<p>You can start your documentation site within minutes. You can deploy the Docsify website with one click on GitHub pages.</p>
<p>The most important thing about Docsify is that you don't need prior knowledge to work with Docsify or any other tools or configuration.</p>
<h3 id="heading-cons-3">Cons</h3>
<ol>
<li>Docsify comes with fewer features, but customization options are available.</li>
<li>Docsify only has a few themes and plugins available on the internet.</li>
<li>Most of the themes you find on the internet are outdated in terms of their UI. </li>
</ol>
<p>To learn more about Docsify, <a target="_blank" href="https://www.freecodecamp.org/news/how-to-write-good-documentation-with-docsify/">read my in-depth tutorial on freeCodeCamp</a>. </p>
<h2 id="heading-markdoc">Markdoc</h2>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/01/markdoc.png" alt="Mark doc" width="600" height="400" loading="lazy">
<em>Mark doc</em></p>
<p><strong><a target="_blank" href="https://markdoc.dev/">Markdoc</a></strong> is an open-source, powerful, and flexible Markdown-based framework. Markdoc is built and maintained by the Stripe team. With Markdoc, you can develop your personal blogs and documentation sites. </p>
<p>Markdoc comes with several built-in features, such as:</p>
<ol>
<li>It's lightweight</li>
<li>There's built-in syntax validation</li>
<li>It has support for partials</li>
<li>You can extend Markdoc with custom functions</li>
<li>It supports tags </li>
<li>You can customize styles with annotations</li>
<li>It supports variables and attributes</li>
</ol>
<p>Markdoc is developer and writer-friendly. You can build interactive documentation and static content sites using pure HTML, Next.js, and React.js. </p>
<h3 id="heading-cons-4">Cons</h3>
<ol>
<li>To work with makdoc, you must write the entire website code from scratch.</li>
<li>Markdoc is not for beginner developers. You must know some advanced JavaScript and React concepts when working with Markdoc.</li>
</ol>
<h2 id="heading-contentlayer">Contentlayer</h2>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/01/content-layer.png" alt="Content layer" width="600" height="400" loading="lazy">
<em>Content layer</em></p>
<p><strong><a target="_blank" href="https://contentlayer.dev/">Contentlayer</a></strong> is an open-source content SDK that validates and transforms your content into type-safe JSON data so you can easily use it with your existing frameworks, such as Next.js.</p>
<p>The best part about Contentlayer is that you can build a type-safe schema for your blog and documentation. </p>
<p>Contentlayer works similarly to Markdoc – you must write the documentation and maintain the code base.</p>
<p>There are many helpful features, but here are a few:</p>
<ol>
<li>It's framework agnostic</li>
<li>It's built to be very fast</li>
<li>Makes it easier to parse content on your site</li>
<li>You can use JavaScript/TypeScript – no new query language required</li>
<li>It has automatic content and frontmatter validation</li>
</ol>
<h3 id="heading-cons-5">Cons</h3>
<ol>
<li>Contentlayer comes with support for a limited number of frameworks.</li>
<li>You must write website code from scratch to work with the Content layer.</li>
</ol>
<p>Read <a target="_blank" href="https://officialrajdeepsingh.medium.com/list/create-static-blog-with-nextjs-and-markdown-34cbab11b5ed">my in-depth tutorial on Medium</a> to learn more about Contentlayer<a target="_blank" href="https://officialrajdeepsingh.medium.com/list/create-static-blog-with-nextjs-and-markdown-34cbab11b5ed">.</a> </p>
<h2 id="heading-gitbook">Gitbook</h2>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/01/gitbook.png" alt="Git book" width="600" height="400" loading="lazy">
<em>Git book</em></p>
<p><a target="_blank" href="https://www.gitbook.com/"><strong>Gitbook</strong></a> is not open-source, but it comes with free and paid plans. Gitbook is built and designed to manage documentation. You can even develop your API and service documentation website with Gitbook. </p>
<p>Git Book comes with various built-in features, such as:</p>
<ol>
<li>It has a no-code solution</li>
<li>You can easily integrate it with other applications</li>
<li>You can customize and change the theme with one click</li>
<li>It's easy to use it to collaborate with your team</li>
<li>It has a powerful block-based editor</li>
<li>You can embed code your demo code with code sandbox IDE</li>
<li>It has built-in search support</li>
<li>It has built-in SEO, sitemap and caching &amp; CDN support</li>
</ol>
<p>Gitbook comes with a no-code solution – you do need to write a single line of code to create a documentation site. Gitbook provides you with a modern feel for creating documentation without writing code.</p>
<p>You can integrate Gitbook with other services like GitHub. And you do not need to worry about deploying Gitbook – it does all the hard work for you. With one click, you can focus on writing and designing or changing themes in your documentation in Gitbook.</p>
<h3 id="heading-cons-6">Cons</h3>
<ol>
<li>Many features like theme customization and team management come with the paid plan.</li>
</ol>
<h2 id="heading-outstatic-cms">Outstatic CMS</h2>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/01/outstatic-cms.png" alt="Outstatic CMS" width="600" height="400" loading="lazy">
<em>Outstatic CMS</em></p>
<p><a target="_blank" href="https://outstatic.com/"><strong>Outstatic CMS</strong></a> is a Next.js-based open-source static CMS that helps you manage your content with the help of GitHub. </p>
<p>Outstatic cms comes with several built-in features, such as:</p>
<ol>
<li>There's an AI completion option</li>
<li>You can manage your content with custom fields</li>
<li>It's quick and easy to setup</li>
<li>You don't need a database</li>
<li>It's a modern content editor</li>
</ol>
<p>Using Outstatic CMS, you can easily publish, update, and remove the content using the dashboard and editor. This is helpful if you don't know how to use Markdown and some who depend on grammar tools, for example, Grammarly, Turnitin, Quillbot, and so on.  </p>
<p>Outstatic CMS only works with Next.js and GitHub. Outstatic directly creates content inside your GitHub repository using Github API.</p>
<h3 id="heading-cons-7">Cons</h3>
<ol>
<li>You need to write website code design to build and test from scratch.</li>
<li>Outstatic CMS does not work offline.</li>
</ol>
<p><a target="_blank" href="https://medium.com/frontendweb/start-the-static-blog-website-with-outstatic-cms-in-2024-a909c12318f0">Read my in-depth tutorial on Medium</a> to learn more about it. </p>
<h2 id="heading-code-doc">Code doc</h2>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/01/codedoc-1.png" alt="Code Doc" width="600" height="400" loading="lazy">
<em>Code Doc</em></p>
<p><strong><a target="_blank" href="https://codedoc.cc">Code Doc</a></strong> is an open-source, simple, lightweight, easy-to-configure documentation generator tool that helps create beautiful and modern software documentation websites within minutes. </p>
<p>Code Doc comes with several built-in features, such as:</p>
<ol>
<li>It's simple and lightweight</li>
<li>It's easy to customize and configure</li>
<li>It has an enhanced markdown and code block experience</li>
<li>It has integrated search and dark mode</li>
<li>You can extend the functionality with the Code doc Plugin API</li>
<li>You can build your own custom components</li>
</ol>
<p>With code doc, you focus on your documentation writing rather than writing and maintaining your codebase.  But Code Doc is easy to customize and configure. You do not need prior knowledge to use it.</p>
<p>The most important thing about Code Doc is that it has more modern UI features than Docsify.</p>
<h3 id="heading-cons-8">Cons</h3>
<ol>
<li>A Code Doc project or repository cannot be actively maintained by its developer.</li>
</ol>
<h2 id="heading-front-matter">Front Matter</h2>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/01/Front-Matter.png" alt="Front Matter CMS" width="600" height="400" loading="lazy">
<em>Front Matter CMS</em></p>
<p><a target="_blank" href="https://frontmatter.codes/"><strong>Front Matter</strong></a> is a Headless CMS right in your Visual Studio Code. Front matter gives a helping hand to technical writers and coders. </p>
<p>Front Matter cms comes with several built-in features, such as:</p>
<ol>
<li>It works seamlessly with any static site generator</li>
<li>It's fully configurable</li>
<li>It brings the features/benefits of a CMS to your VS Code </li>
<li>You get a full site/page preview within VS Code</li>
<li>You can manage your content, media, snippets, and data with VS Code</li>
<li>You can edit your metadata</li>
<li>You can check your SEO status in VS Code</li>
</ol>
<p>The tool works inside VS Code, letting you edit, write, update, and delete documentation in your VS Code without ever having to leave the editor. You can write your documentation using Markdown and VS Code. </p>
<p>You can also edit your metadata (front matter like title, description, tag, date, and so on) and check the SEO status in VS Code.</p>
<p>The best part is integrating Front Matter with other tools or libraries, such as Nextra, Docusaurus, Lume, and Docsify, to enhance the developer's writing experience using VS Code.</p>
<h3 id="heading-cons-9">Cons</h3>
<ol>
<li>You cannot use Front Matter CMS with other IDEs like Vim, Neovim, Atom, Sublime Text, JetBrains IDEs, and so on.</li>
<li>Front Matter CMS is not useful for everyone. Front matter CMS Only targets software developers who will find it very useful.</li>
</ol>
<p><a target="_blank" href="https://medium.com/frontendweb/what-is-frontmatter-headless-cms-and-how-to-use-it-with-nextjs-b764b76718ea">Read my in-depth tutorial on Medium</a> to learn more about the Front Matter CMS.</p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>Without documentation, your product or service will never be successful. Spend equal time on one code base and as well on documentation. </p>
<p>For quick and short-term documentation, I recommended Git Book, nextra, and Docusaurus. If you have time and teams, go with Outstatic CMS, Content layer, and Mark doc. Lastly, you do not know JavaScript and reactjs. You can go with Git Book and Docsify.</p>
<p>I am not recommending the Code Doc library due to inactive maintenance by its developer. I'm not sure if the code doc was abandoned by its developer or not.</p>
<p>You can hire me as a freelance developer with <a target="_blank" href="https://www.upwork.com/freelancers/~01a4e8ba7a41795229">Upwork</a> and other updates. Follow me on <a target="_blank" href="https://twitter.com/Official_R_deep">Twitter (X)</a> and <a target="_blank" href="https://officialrajdeepsingh.medium.com/">Medium</a>.</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Write Good Documentation with Docsify ]]>
                </title>
                <description>
                    <![CDATA[ Documentation is critical to a successful product. Without documentation, it's more difficult for people to use your product, and it's just as important if you're running an open-source project, too. Creating a documentation site can be challenging, ... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-write-good-documentation-with-docsify/</link>
                <guid isPermaLink="false">66d038b87d662cf201f5ec5d</guid>
                
                    <category>
                        <![CDATA[ documentation ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Rajdeep Singh ]]>
                </dc:creator>
                <pubDate>Thu, 26 Oct 2023 19:21:30 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2023/10/docsify.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Documentation is critical to a successful product. Without documentation, it's more difficult for people to use your product, and it's just as important if you're running an open-source project, too.</p>
<p>Creating a documentation site can be challenging, especially if you're not familiar with front-end development. </p>
<p>I have been a front-end developer for the past 8 years. During that time, I have used many frameworks to build documentation, like <a target="_blank" href="https://nextjs.org/">Next.js</a>, <a target="_blank" href="https://nextra.site/">nextra</a>, <a target="_blank" href="https://contentlayer.dev/">content layer</a>, <a target="_blank" href="https://ghost.org/">Ghost CMS</a>, <a target="_blank" href="https://lume.land/">lume (deno)</a>, <a target="_blank" href="https://docusaurus.io/">docusaurus</a>, and <a target="_blank" href="https://markdoc.dev/">Mark doc</a>.</p>
<p>But to use many of these, you need to have essential knowledge about JavaScript, Next.js, and React. You may run into some challenges, like:</p>
<ol>
<li>Lack of knowledge of JavaScript, React, or other necessary tools</li>
<li>Documentation versioning </li>
<li>Configuration </li>
<li>Deployment</li>
</ol>
<p>In this guide, I'll introduce you to a powerful tool that can help you write documentation without needing as much technical knowledge.</p>
<h2 id="heading-what-is-docsify">What is Docsify?</h2>
<p>To help you solve this problem, I'd recommend a tool called <a target="_blank" href="https://docsify.js.org/#/"><strong>docsify</strong></a>. Docsify is a simple and lightweight documentation generator. You can start using it without having any knowledge of JavaScript or React. </p>
<p>Docsify comes with zero configuration, no statically built HTML files, multiple theme support, inbuilt plugin API, and full-text search support with a plugin. It also deploys on a wide range of platforms like GitHub pages, GitLab Pages, Firebase Netlify, Vercel, and others. </p>
<p>I created a demo project to show you how to use it – the <a target="_blank" href="https://github.com/officialrajdeepsingh/docsifyjs">source code is available</a> on GitHub. You can also <a target="_blank" href="https://officialrajdeepsingh.github.io/docsifyjs/#/">check out the live demo site</a>.</p>
<h3 id="heading-how-to-setup-and-use-docsify">How to Setup and Use Docsify</h3>
<p>You can create a new project with the <a target="_blank" href="https://cli.docsifyjs.org/">docsify-cli</a>. To use docsify-cli, you need to install Node and NPM if you don't already have them installed. </p>
<pre><code class="lang-bash">npm install -g docsify-cli
<span class="hljs-comment"># or</span>
yarn add -g docsify-cli
<span class="hljs-comment"># or</span>
pnpm add -g docsify-cli
</code></pre>
<p>The command output looks like this:</p>
<pre><code class="lang-bash">❯ pnpm add -g docsify-cli

Packages: +198
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Progress: resolved 199, reused 114, downloaded 84, added 198, <span class="hljs-keyword">done</span>
.pnpm/docsify@4.13.1/node_modules/docsify: Running postinstall script, <span class="hljs-keyword">done</span> <span class="hljs-keyword">in</span> 196ms

/home/rajdeepsingh/.<span class="hljs-built_in">local</span>/share/pnpm/global/5:
+ docsify-cli 4.4.4
+ pnpm 8.7.0

Done <span class="hljs-keyword">in</span> 13.9s
</code></pre>
<p>Create your new project with the docsify-cli init option.</p>
<pre><code>➜ docsify init docs

Initialization succeeded! Please run docsify serve ./docs
</code></pre><p>You can also specify the <code>--theme</code> and <code>--plugins</code>.</p>
<pre><code class="lang-bash">➜ docsify init docs --theme buble --plugins
</code></pre>
<p>You can read more about <a target="_blank" href="https://cli.docsifyjs.org/">docsify-cli</a> on the documentation page.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/09/docsify-plugins.gif" alt="Install the plugins with the docsify init option." width="600" height="400" loading="lazy">
<em>Install the plugins with the docsify init option.</em></p>
<p>Next, start your local development server using docsify-cli. For that, run the following command:</p>
<pre><code class="lang-bash">docsify serve docs
<span class="hljs-comment"># or</span>
docsify serve .
</code></pre>
<p> Your local server runs on a 3000 port locally.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/09/Screenshot-from-2023-09-20-22-37-52.png" alt="Image" width="600" height="400" loading="lazy">
<em>Run docsify serve</em></p>
<p>Your website should look like this when you open it in the browser:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/09/docsify-demo.png" alt="Demo docsify" width="600" height="400" loading="lazy">
<em>Demo Doctify</em></p>
<h2 id="heading-docsify-folder-structure">Docsify Folder Structure</h2>
<p>Docsify has a straightforward folder structure. By default, when you create a new project with <a target="_blank" href="https://cli.docsifyjs.org/">docsify-cli</a>, there are three main files:</p>
<pre><code class="lang-bash">├── index.html // This is an HTML entry file.
├── .nojekyll  // This is helpful when you deploy your project on GitHub pages.
└── README.md  // This is the home page or / router
</code></pre>
<h2 id="heading-how-to-customize-docsify">How to Customize Docsify</h2>
<p>Docsify comes with lots of customization options, and you don't need any additional knowledge to configure it – it's pretty straightforward, just like copying and pasting code. </p>
<p>In this guide, we'll explore some of the most common customization options. For advance configuration, you can check out the Docsify documentation.</p>
<ol>
<li><a class="post-section-overview" href="#heading-basic-configuration">Basic configuration</a></li>
<li><a class="post-section-overview" href="#heading-loading-screen">Loading screen</a></li>
<li><a class="post-section-overview" href="#heading-sidebar">Sidebar</a></li>
<li><a class="post-section-overview" href="#heading-header">Header</a></li>
<li><a class="post-section-overview" href="#heading-cover-page">Cover Page</a></li>
<li><a class="post-section-overview" href="#heading-plugins">Plugins</a></li>
<li><a class="post-section-overview" href="#heading-themes">Themes</a></li>
<li><a class="post-section-overview" href="#heading-deployment">Deployment</a></li>
</ol>
<h3 id="heading-basic-configuration">Basic configuration</h3>
<p>In the basic configuration, you can change or add a logo, a name, add your GitHub repository link, a theme color, and so on.</p>
<p>Here's the code to do that – you can fill in your own details.</p>
<pre><code class="lang-html"> <span class="hljs-comment">&lt;!-- index.html --&gt;</span>
 <span class="hljs-tag">&lt;<span class="hljs-name">script</span>&gt;</span><span class="xml">
      window.$docsify = {
        logo: '/_media/icon.svg',  <span class="hljs-comment">&lt;!-- add logo --&gt;</span>
        name: "Document",  <span class="hljs-comment">&lt;!-- Website name it appears in sidebar. --&gt;</span>
        nameLink: '/',  <span class="hljs-comment">&lt;!-- url for name --&gt;</span>
        repo: "officialrajdeepsingh/docsifyjs",<span class="hljs-comment">&lt;!--github repository--&gt;</span>
        maxLevel: 2,  <span class="hljs-comment">&lt;!-- Maximum Table of content level. --&gt;</span>
        themeColor: '#3F51B5', <span class="hljs-comment">&lt;!-- Customize theme color --&gt;</span>
      };
</span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span>
</code></pre>
<h3 id="heading-loading-screen">Loading screen</h3>
<p>Enabling a loading screen or dialogue is typically very tricky, especially if you come from the JavaScript and React.js ecosystem.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/10/ezgif.com-video-to-gif-2.gif" alt="Loading screen in Docsify" width="600" height="400" loading="lazy">
<em>Loading screen in Docsify</em></p>
<p>In Docsify, you can <a target="_blank" href="https://docsify.js.org/#/quickstart?id=loading-dialog">enable a loading screen</a> without any configuration. You just simply write some text along with an HTML element inside your app ID, which will then show as a loading screen. It looks like this:</p>
<pre><code class="lang-html"><span class="hljs-comment">&lt;!-- index.html --&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"app"</span>&gt;</span>Please wait...<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>

<span class="hljs-comment">&lt;!-- or --&gt;</span>

<span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"app"</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">h1</span>&gt;</span> Please wait... <span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
</code></pre>
<h3 id="heading-sidebar">Sidebar</h3>
<p>By default, the sidebar shows the table of contents. But you can customize it very easily. First, you'll need to <a target="_blank" href="https://docsify.js.org/#/more-pages?id=sidebar">enable the sidebar</a>.</p>
<pre><code class="lang-html"><span class="hljs-comment">&lt;!-- index.html --&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">script</span>&gt;</span><span class="xml">
   window.$docsify = {

        loadSidebar: true, <span class="hljs-comment">&lt;!-- Enable sidebar --&gt;</span>

   };
</span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span>
</code></pre>
<p>Then create a new <code>_sidebar.md</code> file at the root level and paste the following code:</p>
<pre><code class="lang-_sidebar.md">- [Home](README.md)
- [Draft Article](draft-article.md)
- [Guide](guide.md)
- [First](page-first.md)
- [Second](page-second.md)
- [Third](page-third.md)
- [Four](page-four.md)
</code></pre>
<p>Your sidebar should now look like this:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/10/customizer-sidebar.png" alt="Your sidebar looks like this." width="600" height="400" loading="lazy">
<em>Your sidebar looks like this.</em></p>
<h3 id="heading-header">Header</h3>
<p>By default, you won't be able to see the Navbar on your Docsify site:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/10/navbar.png" alt="Image" width="600" height="400" loading="lazy">
<em>No Navar</em></p>
<p>But don't worry, you can change that. To show the <a target="_blank" href="https://docsify.js.org/#/custom-navbar">Navbar, you first have to enable it</a> like this:</p>
<pre><code class="lang-html"><span class="hljs-comment">&lt;!-- index.html --&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">script</span>&gt;</span><span class="xml">
      window.$docsify = {


        loadNavbar: true,     <span class="hljs-comment">&lt;!-- enable navbar --&gt;</span>

      };
    </span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span>
  <span class="hljs-tag">&lt;/<span class="hljs-name">body</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">html</span>&gt;</span>
</code></pre>
<p>Then create a new <code>_navbar.md</code> file at the root level and paste the following code:</p>
<pre><code class="lang-markdown"><span class="hljs-bullet">*</span> Getting started

<span class="hljs-bullet">  *</span> [<span class="hljs-string">Quick start</span>](<span class="hljs-link">quickstart.md</span>)
<span class="hljs-bullet">  *</span> [<span class="hljs-string">Writing more pages</span>](<span class="hljs-link">more-pages.md</span>)
<span class="hljs-bullet">  *</span> [<span class="hljs-string">Custom navbar</span>](<span class="hljs-link">custom-navbar.md</span>)
<span class="hljs-bullet">  *</span> [<span class="hljs-string">Cover page</span>](<span class="hljs-link">cover.md</span>)

<span class="hljs-bullet">*</span> Configuration
<span class="hljs-bullet">  *</span> [<span class="hljs-string">Configuration</span>](<span class="hljs-link">configuration.md</span>)
<span class="hljs-bullet">  *</span> [<span class="hljs-string">Themes</span>](<span class="hljs-link">themes.md</span>)
<span class="hljs-bullet">  *</span> [<span class="hljs-string">Using plugins</span>](<span class="hljs-link">plugins.md</span>)
<span class="hljs-bullet">  *</span> [<span class="hljs-string">Markdown configuration</span>](<span class="hljs-link">markdown.md</span>)
<span class="hljs-bullet">  *</span> [<span class="hljs-string">Language highlight</span>](<span class="hljs-link">language-highlight.md</span>)
</code></pre>
<p>Your Navbar should now look like this:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/10/customizer-navbar.png" alt="Image" width="600" height="400" loading="lazy">
<em>Customize Navbar in Docsify</em></p>
<h3 id="heading-cover-page">Cover Page</h3>
<p>First, <a target="_blank" href="https://docsify.js.org/#/cover">enable the cover page</a> in docsify with the following code:</p>
<pre><code class="lang-html"><span class="hljs-comment">&lt;!-- index.html --&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">script</span>&gt;</span><span class="xml">
      window.$docsify = {

        coverpage: true,     <span class="hljs-comment">&lt;!-- enable coverpage --&gt;</span>

      };
</span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span>
</code></pre>
<p>The next step is to create a new <code>_coverpage.md</code> file.</p>
<pre><code class="lang-markdown"><span class="hljs-section"># Learn Docsify </span>
<span class="hljs-section">### Learn the docsify start from beginner.</span>

[<span class="hljs-string">Start Learn</span>](<span class="hljs-link"></span>)
[<span class="hljs-string">Github</span>](<span class="hljs-link">#/README</span>)
</code></pre>
<p>Your website cover page should look like this:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/10/coverpage.png" alt="Image" width="600" height="400" loading="lazy">
<em>Your cover page</em></p>
<p>The cover page and your UI depend on the theme, so they'll differ from theme to theme. </p>
<h3 id="heading-plugins">Plugins</h3>
<p>Plugins help provide additional functionality and features to Dicsify and enhance the user experience as well. </p>
<p>You can create and use plugins according to your own requirements. <strong>[D</strong>ocsify<strong>](https://github.com/docsifyjs/awesome-docsify)</strong> has many plugins available that are open-source and created by various contributors. </p>
<p>You can use any plugin by just copy-pasting the code. Even you can create your own plugins with docsify.</p>
<h4 id="heading-how-to-use-third-party-plugins">How to use third-party plugins</h4>
<p>In this example, we'll enable the search bar functionally with the help of the docsify plugin.</p>
<p>To enable the search bar, copy and paste the following script inside your <code>index.html</code> file:</p>
<pre><code class="lang-javascript">&lt;script src=<span class="hljs-string">"//cdn.jsdelivr.net/npm/docsify/lib/plugins/search.min.js"</span>&gt;&lt;/script&gt;
</code></pre>
<p>Now the search bar will appear and work on your site. With the search plugin, you can also configure various functionality – read more about it on the <a target="_blank" href="https://docsify.js.org/#/plugins?id=full-text-search">search plugin installation and configure page</a>.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/10/searchbar.png" alt="Image" width="600" height="400" loading="lazy">
<em>Docsify Search plugin</em></p>
<h4 id="heading-how-to-create-your-own-plugin-with-docsify">How to create your own plugin with docsify</h4>
<p>To create your own plugin in docsify, there's an inbuilt hook you'll need to use for the plugin.</p>
<p>Docsify has six inbuilt hooks: <code>init</code>, <code>mounted</code>, <code>beforeEach</code>, <code>afterEach</code>, <code>doneEach</code>, and <code>ready</code>. </p>
<ol>
<li><code>init</code>: gets invoked one time when the docsify script is initialized.</li>
<li><code>mounted</code>: gets invoked one time when the docsify instance has mounted on the DOM.</li>
<li><code>beforeEach</code>: gets invoked on each page load before the new markdown is transformed to HTML.</li>
<li><code>afterEach</code>: called on each page load after the markdown has been turned into HTML.</li>
<li><code>doneEach</code>: gets invoked on each page load after new HTML has been appended to the DOM.</li>
<li><code>ready</code>: gets invoked one time after rendering the initial page.</li>
</ol>
<p>With the help of these hooks, you can create a plugin. To learn more about creating your own plugins, check out the <a target="_blank" href="https://docsify.js.org/#/write-a-plugin?id=template">custom plugin document page</a>.</p>
<p>In this example, we'll create an edit button using the beforeEach plugin hook. It shows the EDIT DOCUMENT button on every page.</p>
<pre><code class="lang-html"><span class="hljs-comment">&lt;!-- index.html --&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">script</span>&gt;</span><span class="javascript">
      <span class="hljs-built_in">window</span>.$docsify = {

        <span class="hljs-attr">plugins</span>: [

        &lt;!-- write own custom plugin --&gt;
        <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">editButton</span>(<span class="hljs-params">hook, vm</span>) </span>{

          <span class="hljs-comment">// call the hook</span>
          hook.beforeEach(<span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">html</span>) </span>{

            <span class="hljs-keyword">var</span> url = <span class="hljs-string">"https://github.com/officialrajdeepsingh/docsifyjs/edit/master/"</span> + vm.route.file;

              <span class="hljs-comment">// basic route fix </span>
            <span class="hljs-keyword">let</span> tempFile = url.replace(<span class="hljs-string">"docsifyjs/README.md"</span>, <span class="hljs-string">"README.md"</span>,)
              ? url.replace(<span class="hljs-string">"docsifyjs/README.md"</span>, <span class="hljs-string">"README.md"</span>)
              : url;

            <span class="hljs-comment">// Add Edit Button</span>
            <span class="hljs-keyword">var</span> editHtml = <span class="hljs-string">"[📝 EDIT DOCUMENT]("</span> + url + <span class="hljs-string">")\n"</span>;

            <span class="hljs-comment">// Add edit button on top of file</span>
            <span class="hljs-keyword">return</span> editHtml + html + <span class="hljs-string">"\n----\n"</span> + <span class="hljs-string">"Last modified "</span> + editHtml;
          });
        },
        ],


      };
    </span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span>
</code></pre>
<h3 id="heading-themes">Themes</h3>
<p>Docsify has both <a target="_blank" href="https://docsify.js.org/#/themes?id=themes">official and community-made themes</a>. You can use any of them, and the good part is you do not need to write any extra code when you switch themes. </p>
<pre><code class="lang-html"><span class="hljs-comment">&lt;!--vue theme --&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">link</span> <span class="hljs-attr">rel</span>=<span class="hljs-string">"stylesheet"</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"//cdn.jsdelivr.net/npm/docsify/themes/vue.css"</span> /&gt;</span>

<span class="hljs-comment">&lt;!-- buble theme --&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">link</span> <span class="hljs-attr">rel</span>=<span class="hljs-string">"stylesheet"</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"//cdn.jsdelivr.net/npm/docsify/themes/buble.css"</span> /&gt;</span>

<span class="hljs-comment">&lt;!-- dark theme --&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">link</span> <span class="hljs-attr">rel</span>=<span class="hljs-string">"stylesheet"</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"//cdn.jsdelivr.net/npm/docsify/themes/dark.css"</span> /&gt;</span>

<span class="hljs-comment">&lt;!-- pure theme --&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">link</span> <span class="hljs-attr">rel</span>=<span class="hljs-string">"stylesheet"</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"//cdn.jsdelivr.net/npm/docsify/themes/pure.css"</span> /&gt;</span>
</code></pre>
<p>You can choose to compress or not compress theme CSS files. The compressed CSS file is a minified version of the theme, and it is a lightweight CSS file for production. Other the other hand, a non-compressed theme CSS file is helpful for development.</p>
<p>You just copy the CSS theme (Vue, bubble, dark, and pure) file and paste it inside the head element. And with that, your theme is changed.  </p>
<p>In terms of non-official themes, I think the <a target="_blank" href="https://jhildenbiddle.github.io/docsify-themeable/#/">docsify-themeable</a> theme is the best option for you.</p>
<h3 id="heading-deployment">Deployment</h3>
<p>Docsify has various options for deployment. You can deploy your Docsify site on GitHub pages, <a target="_blank" href="https://docsify.js.org/#/deploy?id=gitlab-pages">GitLab Pages</a>, <a target="_blank" href="https://docsify.js.org/#/deploy?id=firebase-hosting">Firebase Hosting</a>, <a target="_blank" href="https://docsify.js.org/#/deploy?id=vps">VPS</a> (Nginx), <a target="_blank" href="https://docsify.js.org/#/deploy?id=netlify">Netlify</a>, <a target="_blank" href="https://docsify.js.org/#/deploy?id=vercel">Vercel</a>, <a target="_blank" href="https://docsify.js.org/#/deploy?id=aws-amplify">AWS Amplify</a>, and <a target="_blank" href="https://docsify.js.org/#/deploy?id=docker">Docker</a>.</p>
<p>In some platforms like GitHub pages, you deploy your docsify site directly with the GitHub repository without writing any configuration.</p>
<p>Here's the process to do that:</p>
<p>You'll go to <strong>Settings</strong> &gt; <strong>Pages</strong> &gt; <strong>Source</strong> &gt; and then select Deploy from a branch &gt; <strong>Branch</strong> &gt; Select your branch with folder and click on the save button.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/10/github-deploy.png" alt="Deploy the docsify with GitHub pages" width="600" height="400" loading="lazy">
<em>Deploy docsify with GitHub pages</em></p>
<p>It will take some time, depending on your website size. After deployment finishes, you should see your production URL. </p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/10/deploy-github-pages.png" alt="Finish the deployment" width="600" height="400" loading="lazy">
<em>Finish the deployment</em></p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>Docsify is a powerful tool for generating documentation sites. In docsify, you can focus on documentation writing, not UI design. </p>
<p>Docsify is a good option for developers who aren't that familiar with JavaScript. If you focus more on low-level languages like C++ or rust, docsify can help you get started writing your documentation with one command.</p>
<p>I recently used docsify for my <a target="_blank" href="https://github.com/officialrajdeepsingh/awesome-nextjs">awesome-nextjs</a> repository. You can easily deploy on the GitHub page without any configuration. </p>
<p>Just keep in mind that there are two <strong>downsides</strong> to docsify:</p>
<ol>
<li>Docsify does not generate dynamic SEO meta tags for a page. It only generates a title and description.</li>
<li>The docsify theme does not provide a modern UI feel.</li>
</ol>
<p>But it's still very useful! Enjoy creating your documentation :)</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ The Best Gnome Extensions For Developer Productivity ]]>
                </title>
                <description>
                    <![CDATA[ If you're a beginner who's using a Gnome-based distro like Ubuntu, Fedora, Arch Linux, and so on, there are tons of extensions available from the Gnome community. Gnome is an open-source Linux-based distro desktop environment (operating system) maint... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/the-best-gnome-extensions-for-developers/</link>
                <guid isPermaLink="false">66d038bd871ae63f179f6bdb</guid>
                
                    <category>
                        <![CDATA[ Linux ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Productivity ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Rajdeep Singh ]]>
                </dc:creator>
                <pubDate>Thu, 22 Jun 2023 22:02:31 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2023/06/The-Best-Gnome-Extensions-For-Developer-Productivity.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>If you're a beginner who's using a Gnome-based distro like Ubuntu, Fedora, Arch Linux, and so on, there are tons of extensions available from the Gnome community.</p>
<p>Gnome is an open-source Linux-based distro desktop environment (operating system) maintained by a large number of developers.</p>
<p>Choosing the right Gnome extension(s) can be tricky, and a tad overwhelming. To help you out, in this guide I'll go over six Gnome extensions that most users install, irrespective of their experience with Gnome.</p>
<h2 id="heading-table-of-contents">Table of Contents</h2>
<ol>
<li><a class="post-section-overview" href="#heading-clipboard-history">Clipboard History</a></li>
<li><a class="post-section-overview" href="#heading-extension-list">Extension List</a></li>
<li><a class="post-section-overview" href="#heading-freon">Freon</a></li>
<li><a class="post-section-overview" href="#heading-net-speed">Net Speed</a></li>
<li><a class="post-section-overview" href="#heading-privacy-quick-settings">Privacy Quick Settings</a></li>
<li><a class="post-section-overview" href="#heading-dim-on-battery-power">Dim On Battery Power</a></li>
</ol>
<h2 id="heading-clipboard-history">Clipboard History</h2>
<p>As a developer, you'll often copy and paste stuff, and sometimes it gets messy. In some cases, you might lose what you've copied or copy the wrong text.</p>
<p>The <a target="_blank" href="https://extensions.gnome.org/extension/4839/clipboard-history/">Clipboard History</a> Gnome extension helps you manage or preserve your clipboard history. </p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/06/clip-board.gif" alt="Manage your clipboard history" width="600" height="400" loading="lazy">
<em>Manage your clipboard history.</em></p>
<p>With this extension, you get your old clipboard (copy-paste) history and you can reuse it as well. The Clipboard History extension comes with many cool features that you can change in the extension settings tab.</p>
<p>For example, you can change the panel width, remove the white space, change the max clipboard history size, and more.</p>
<h2 id="heading-extension-list">Extension List</h2>
<p>Gnome users install many extensions to increase productivity and improve the user experience with the Gnome desktop.</p>
<p>The problem occurs when you install lots of extensions, and managing them can be a difficult task.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/06/extension-list.gif" alt="Extension List gnome extension" width="600" height="400" loading="lazy">
<em>Extension List gnome extension</em></p>
<p>The <a target="_blank" href="https://extensions.gnome.org/extension/3088/extension-list/">Extension List extension</a> gives you a list of installed extensions, and you can easily turn them on/off, manage settings, and uninstall extensions.</p>
<h2 id="heading-freon">Freon</h2>
<p>All Linux distro systems are power-consuming machines, and you may have information about temperature, fan RPM, and voltage on the home screen. When the temperature is high, actions like closing some running apps or not using the laptop for some time can help to reduce the temperature.   </p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/06/freon.gif" alt="Freon gnome extension" width="600" height="400" loading="lazy">
<em>Freon gnome extension</em></p>
<p>The <a target="_blank" href="https://extensions.gnome.org/extension/1180/freon/">Freon extension</a> is helpful for Gnome users because it gives you operating system information about the CPU, disk, video card temperature, voltage, and fan RPM on the distro home screen in Gnome.</p>
<h2 id="heading-net-speed">Net Speed</h2>
<p>Internet or connectivity is an important factor for developers. Without the Internet, we cannot work. Sometimes you can't tell if the internet speed is good or bad. For example, in India, network connectivity is a big issue.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/06/net-speed.png" alt="Net Speed gnome extension" width="600" height="400" loading="lazy">
<em>Net Speed gnome extension</em></p>
<p>To solve this issue, the <a target="_blank" href="https://extensions.gnome.org/extension/4478/net-speed/">Net Speed Gnome</a> extension is the best. It gives you the current net speed on your desktop home screen.</p>
<h2 id="heading-privacy-quick-settings">Privacy Quick Settings</h2>
<p>It is important to secure yourself on the internet, especially as a user. Sometimes we forget which access we've enabled on the Gnome desktop, such as locations, microphones, and cameras.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/06/privacy.gif" alt="Privacy Quick Settings gnome extension" width="600" height="400" loading="lazy">
<em>Privacy Quick Settings gnome extension</em></p>
<p>The <a target="_blank" href="https://extensions.gnome.org/extension/4491/privacy-settings-menu/">Privacy Quick Settings</a> Gnome extension is useful because it gives you a panel or icon on the home screen where you can see which access you've enabled, and you can easily update those privacy settings as needed.</p>
<h2 id="heading-dim-on-battery-power">Dim On Battery Power</h2>
<p>Just like I do, some developers like to work with full brightness on their PC. I find that it's easier on my eyes. </p>
<p>Sometimes, you may forget to charge your laptop while working, and this may result in the laptop or machine switching off because of a dead battery.</p>
<p>The <a target="_blank" href="https://extensions.gnome.org/extension/947/dim-on-battery-power/">Dim On Battery Power</a> Gnome extension can help reduce brightness when the machine is running low on battery power, and it gives you a notification that you need to charge the machine so you can easily know when your laptop's battery power is low.</p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>You'll find my extension list useful whether you're a newbie or a pro at using Gnome. It gives you a starting point, and you should easily be able to install all the extensions I mentioned in the list without any problems.</p>
<p>You can share and follow me on <a target="_blank" href="https://twitter.com/Official_R_deep">Twitter</a> and <a target="_blank" href="https://www.linkedin.com/in/officalrajdeepsingh/">Linkedin</a>. I write tons of articles related to frontend development and Linux. </p>
<p>If you are interested in those topics, you can follow me on <a target="_blank" href="https://officialrajdeepsingh.medium.com/">Medium</a>, <a target="_blank" href="https://officialrajdeepsingh.dev/">officialrajdeepsingh.dev</a>, join the <a target="_blank" href="https://medium.com/frontendweb">frontend web publication</a> and sign up for my <a target="_blank" href="https://officialrajdeepsingh.medium.com/subscribe">free newsletter</a>.</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Build a Blog with the Ghost API and Next.js ]]>
                </title>
                <description>
                    <![CDATA[ Ghost CMS is a popular content management system that many devs and companies use to host their blogs.  It has many features and an editor that's highly optimized for writing. You can even build different themes using handlebars.js. But if you don't ... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/build-a-blog-website-with-ghost-api-and-nextjs/</link>
                <guid isPermaLink="false">66d0389b41966f84606807e8</guid>
                
                    <category>
                        <![CDATA[ blog ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Ghost ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Next.js ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Rajdeep Singh ]]>
                </dc:creator>
                <pubDate>Thu, 13 Apr 2023 21:14:46 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2023/04/Ghost-API-and-Nextjs--2-.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Ghost CMS is a popular content management system that many devs and companies use to host their blogs. </p>
<p>It has many features and an editor that's highly optimized for writing. You can even build different themes using <strong><a target="_blank" href="https://handlebarsjs.com/">handlebars</a>.js</strong>.</p>
<p>But if you don't know Handlebars, learning it can be a long and difficult process. If you are already a Next.js developer and you don't know Handlebars, creating a new theme for your Ghost-based site can be tough.</p>
<p>In the article, I will teach you how to use Ghost CMS as a backend and Next.js as a frontend. I will guide you through everything related to <a target="_blank" href="https://beta.nextjs.org/docs/getting-started">Nextjs 13 app directory</a> and the Ghost CMS API. </p>
<p>Next.js 13 team currently working on the experimental app folder. Next uses file-based routing with the <code>page</code> directory. The new <code>app</code> directory is based on file system routing and provides additional functionality like layouts, error handling, component loading, and server-side and client-side rending out of the box.</p>
<p>All the code is available on <a target="_blank" href="https://github.com/officialrajdeepsingh/nextjsghostcms">GitHub</a>. You can also check out the live <a target="_blank" href="https://nextjsghostcms.vercel.app/">demo website</a>.</p>
<h2 id="heading-table-of-contents">Table of Contents</h2>
<ol>
<li><a class="post-section-overview" href="#heading-why-use-nextjs-for-the-front-end-and-not-a-ghost-cms-theme">Why Use Next.js for the Front End and Not a Ghost CMS Theme?</a></li>
<li><a class="post-section-overview" href="#heading-project-requirements">Project Requirements</a></li>
<li><a class="post-section-overview" href="#heading-how-to-set-up-ghost-cms">How to Set Up Ghost CMS</a></li>
<li><a class="post-section-overview" href="#heading-how-to-set-up-ghost-cms-with-the-cloud">How to Set Up Ghost CMS with the Cloud</a></li>
<li><a class="post-section-overview" href="#heading-how-to-get-the-blog-template">How to Get the Blog Template</a></li>
<li><a class="post-section-overview" href="#heading-how-to-set-up-nextjs">How to Set Up Next.js</a></li>
<li><a class="post-section-overview" href="#what-to-know-before-following-this-tutorial-">What to know before following this tutorial</a></li>
<li><a class="post-section-overview" href="#heading-folder-structure">Folder Structure</a></li>
<li><a class="post-section-overview" href="#heading-how-to-configure-ghost-cms-and-nextjs">How to Configure Ghost CMS and Next.js</a></li>
<li><a class="post-section-overview" href="#heading-understanding-the-nextjs-13-app-folder">Understanding the Next.js 13 App Folder</a></li>
<li><a class="post-section-overview" href="#heading-demo-data-for-the-project">Demo Data for the Project</a></li>
<li><a class="post-section-overview" href="#heading-how-to-build-the-blog">How to Build the Blog</a></li>
<li><a class="post-section-overview" href="#heading-how-to-build-the-header">How to Build the Header</a></li>
<li><a class="post-section-overview" href="#heading-how-to-build-the-footer">How to Build the Footer</a></li>
<li><a class="post-section-overview" href="#heading-how-to-build-the-layout">How to Build the Layout</a> </li>
<li><a class="post-section-overview" href="#heading-how-to-build-the-homepage">How to Build the Homepage</a></li>
<li><a class="post-section-overview" href="#heading-how-to-build-the-reading-page">How to Build the Reading Page</a></li>
<li><a class="post-section-overview" href="#heading-how-to-build-the-tag-page">How to Build the Tag Page</a> </li>
<li><a class="post-section-overview" href="#heading-how-to-build-the-author-page">How to Build the Author Page</a></li>
<li><a class="post-section-overview" href="#heading-how-to-build-single-pages">How to Build Single Pages</a></li>
<li><a class="post-section-overview" href="#heading-how-to-handle-pagination">How to Handle Pagination</a></li>
<li><a class="post-section-overview" href="#heading-nextjs-seo">Next.js SEO</a></li>
<li><a class="post-section-overview" href="#heading-how-to-enable-search">How to Enable Search</a></li>
<li><a class="post-section-overview" href="#heading-error-handling">Error Handling</a></li>
<li><a class="post-section-overview" href="#heading-how-to-rebuild-your-static-site-with-webhooks">How to Rebuild Your Static Site with Webhooks</a></li>
<li><a class="post-section-overview" href="#heading-conclusion">Conclusion</a></li>
</ol>
<p>In this article, we cover the basics of Next's experimental app directory. Then I'll teach you how to step up Next and Ghost CMS locally and how to integrate Ghost with Next. Lastly, I'll show you how to consume data from the backend (via theGhost CMS API ) and show it on the site with React.js.</p>
<h2 id="heading-why-use-nextjs-for-the-front-end-and-not-a-ghost-cms-theme">Why Use Next.js for the Front End and Not a Ghost CMS Theme?</h2>
<p>There are a few reasons why you might consider using Next as the frontend framework for your blog:</p>
<ol>
<li>Ghost CMS doesn't generate static builds, but Next.js does.</li>
<li>You get increased website speed and performance with Next.js and it now provides built-in SEO support and other optimizations. Ghost doesn't have some of these features.</li>
<li>For React developers, it is easy to build a new blog with Next (since Next is React-based), and you do not need to learn additional tools.</li>
<li>You'll find a few service providers available for Ghost to deploy a Ghost blog with one click. Most of them come with a paid plan while one or two offer a free plan (but these tend to have time and feature limitations). For Next.js, many players are available in the market.</li>
</ol>
<p>Basically, when it comes to static builds and website performance, Ghost doesn't perform as well in either case. The alternative is to use a frontend platform like Next, React, Angular, or Vue.</p>
<p>I chose Next because it's a highly in-demand and popular React framework, and plenty of tools and libraries are built around it. </p>
<p>Note that the current project is not ready for TypeScript, but I'm working on it. Because of this <a target="_blank" href="https://medium.com/frontendweb/basic-explanation-about-the-next-config-js-file-eaa539e1fea3">I disabled TypeScript during build time</a> like this:</p>
<pre><code class="lang-typescript"><span class="hljs-comment">/** @type {import('next').NextConfig} */</span>
<span class="hljs-keyword">const</span> nextConfig = {
  experimental: {
    appDir: <span class="hljs-literal">true</span>,
  },

  typescript: {
    ignoreBuildErrors: <span class="hljs-literal">false</span>,
  },

}

<span class="hljs-built_in">module</span>.<span class="hljs-built_in">exports</span> = nextConfig
</code></pre>
<h2 id="heading-project-requirements">Project Requirements</h2>
<p>To follow along with this tutorial, you'll need basic knowledge of the following packages:</p>
<ol>
<li><a target="_blank" href="https://pnpm.io/">PNPM</a> is a Node.js package manager similar to npm or yarn (you can use any of them that you prefer).</li>
<li><a target="_blank" href="https://www.typescriptlang.org/">TypeScript</a> helps you write type-safe code in JavaScript, and can also help improve productivity. It is not required, though. You can use JavaScript in your project.</li>
<li><a target="_blank" href="https://react.dev/">React.js</a> is a free and open-source front-end JavaScript library for building user interfaces with class and function components.</li>
<li><a target="_blank" href="https://beta.nextjs.org/docs/getting-started">Next.js 13 (app)</a> is based on React and it provides additional functionality like routing, error handling, and layouts.</li>
<li><a target="_blank" href="https://ghost.org/docs/content-api/">Ghost CMS API</a> is an open-source content management system (CMS) similar to WordPress. Ghost is specifically designed and built for blogging. In this project, we'll Ghost as the backend and Next as the frontend. For communication between the backend and frontend development, we'll use the Ghost CMS API. </li>
<li><a target="_blank" href="https://tailwindcss.com/">Tailwind CSS</a> is an open source CSS-based framework similar to <a target="_blank" href="https://getbootstrap.com/">Bootstrap</a>. We'll use Tailwind CSS to design our blog website. </li>
</ol>
<h2 id="heading-how-to-set-up-ghost-cms">How to Set Up Ghost CMS</h2>
<p>The next step is installing Ghost locally, which you can do with one command. First, you need to install <code>ghost-cli</code> globally with pnpm, yarn, or npm.</p>
<pre><code class="lang-bash">pnpm add -g ghost-cli@latest

<span class="hljs-comment"># or</span>

yarn global add ghost-cli@latest

<span class="hljs-comment"># or</span>

npm install ghost-cli@latest -g
</code></pre>
<p>After installing the Ghost CLI, you can create a new Ghost blog project locally with the following command:</p>
<pre><code class="lang-bash">ghost install <span class="hljs-built_in">local</span>
</code></pre>
<p>After the blog installation is finished, you can start your local development server with the <code>ghost start</code> command and your local development serve on <code>http://localhost:2368/ghost</code>.</p>
<h3 id="heading-additional-ghost-cli-commands">Additional Ghost CLI Commands</h3>
<p>There are a few additional commands that are helpful when using the Ghost CLI:</p>
<ul>
<li><code>ghost start</code>: start your server.</li>
<li><code>ghost stop</code> : stop your running Ghost server.</li>
<li><code>ghost help</code> : check the available list of commands.</li>
</ul>
<p><strong>Note:</strong></p>
<p>Make sure your current installation directory is empty before installation. Currently, you are installing Ghost in development mode. For production, you won't follow the same steps.</p>
<h2 id="heading-how-to-set-up-ghost-cms-with-the-cloud">How to Set Up Ghost CMS with the Cloud</h2>
<p>If you run into any problems with Ghost local installation, or maybe it's too complicated and you do not have enough space on your drive, you can use a tool like <a target="_blank" href="https://www.digitalpress.blog/">digital press</a> or any other cloud service like GCP or AWS, Digital Ocean, and so on.</p>
<p>I like digital press because it comes with a free plan. Other cloud services do not provide that, which is why I suggest it.</p>
<h2 id="heading-how-to-get-the-blog-template">How to Get the Blog Template</h2>
<p>Creating a new blog from scratch can be tough. In this tutorial, we'll use a pre-build template from <a target="_blank" href="https://github.com/orgs/frontendweb3">the frontend web</a>. All templates have an open-source MIT license, so you can use them, and you don't need to set up everything.</p>
<p>I picked the <a target="_blank" href="https://github.com/frontendweb3/open-blog">Open-blog</a> template from the frontend web. </p>
<h2 id="heading-how-to-set-up-nextjs">How to Set Up Next.js</h2>
<p>Setting up Next is one of the main parts of this tutorial, where you'll spend time and energy coding, debugging, and deploying the site. </p>
<p>Here are the commands to run depending on whether you're using npx, yarn, or pnpm:</p>
<pre><code class="lang-bash">npx create-next-app@latest --experimental-app

<span class="hljs-comment"># or</span>

yarn create next-app --experimental-app

<span class="hljs-comment"># or</span>

pnpm create next-app --experimental-app
</code></pre>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/03/ghostandnextjs--1-.png" alt="create a new nextjs app." width="600" height="400" loading="lazy">
<em>create a new nextjs app.</em></p>
<p>After completing the installation process, we must install some additional Node packages for the blog.</p>
<p>These Node packages can help you speed up your development process. Make sure to install all the below packages to follow along with this guide:</p>
<h3 id="heading-node-packages-to-install">Node packages to install:</h3>
<ol>
<li><code>pnpm add @tryghost/content-api</code>(required)</li>
<li><code>pnpm add @types/tryghost__content-api</code> (required by TypeScript)</li>
<li><code>pnpm add tailwindcss postcss autoprefixer</code></li>
<li><code>pnpm add  @tailwindcss/typography</code></li>
<li><code>pnpm add react-icons</code></li>
<li><code>pnpm add date-fns</code></li>
<li><code>pnpm add next-themes</code></li>
<li><code>pnpm add @radix-ui/react-popover</code></li>
</ol>
<p>Here's what each of these packages does:</p>
<ul>
<li><a target="_blank" href="https://www.npmjs.com/package/@tryghost/content-api">@tryghost/content-api</a> package is a Ghost JavaScript Client Library for fetching <a target="_blank" href="https://ghost.org/docs/content-api/">content API</a> data.</li>
<li><a target="_blank" href="https://www.npmjs.com/package/@types/tryghost__content-api">@types/tryghost__content-api</a> package contains type definitions for @tryghost/content-api.</li>
<li>TailwindCSS, autoprefixer, and PostCSS are packages required for <a target="_blank" href="https://beta.nextjs.org/docs/styling/tailwind-css">Tailwind CSS</a>.</li>
<li><a target="_blank" href="https://tailwindcss.com/docs/typography-plugin">@tailwindcss/typography</a> package for handling dynamic typography with Tailwind CSS.</li>
<li>The <a target="_blank" href="https://www.npmjs.com/package/next-themes">next-themes</a> package enables themes like switching from dark to light mode on your site.</li>
<li>The <a target="_blank" href="https://www.npmjs.com/package/react-icons">react-icons</a> package provides lots of SVG icons for the project. This way, you do not need to download them manually.</li>
<li><a target="_blank" href="https://www.radix-ui.com/docs/primitives/components/popover#installation">@radix-ui/react-popover</a> is part of the Radix UI ecosystem. I choose the Radix popover component for the design of the search component on the site.</li>
<li><a target="_blank" href="https://www.npmjs.com/package/date-fns">date-fns</a> package helps convert your <code>published_at</code> date into a different date format. </li>
</ul>
<h2 id="heading-what-to-know-before-following-this-tutorial">What to Know Before Following This Tutorial</h2>
<p>Before building this project, I highly recommend watching some tutorials on YouTube (especially if you're a beginner with Next.js). These will help you understand some basics about the Next.js experimental app folder. </p>
<p>Every video explains the same kind of topic. If you watch each of the four videos, you have a basic idea of how the Next.js app folder works. That will make this advanced tutorial easier to follow.</p>
<h3 id="heading-vercelhttpswwwyoutubecomvercelhq"><a target="_blank" href="https://www.youtube.com/@VercelHQ">Vercel</a></h3>
<div class="embed-wrapper">
        <iframe width="560" height="315" src="https://www.youtube.com/embed/gSSsZReIFRk" style="aspect-ratio: 16 / 9; width: 100%; height: auto;" title="YouTube video player" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen="" loading="lazy"></iframe></div>
<p>In this tutorial, Lee Robinson covers the basics of routing, dynamic route segments, data fetching, caching, and metadata.</p>
<h3 id="heading-sakura-devhttpswwwyoutubecomsakuradev"><a target="_blank" href="https://www.youtube.com/@SakuraDev">Sakura Dev</a></h3>
<div class="embed-wrapper">
        <iframe width="560" height="315" src="https://www.youtube.com/embed/6htDA6v4FPM" style="aspect-ratio: 16 / 9; width: 100%; height: auto;" title="YouTube video player" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen="" loading="lazy"></iframe></div>
<p>Sakura Dev teaches you about the difference between Next.js pages and the app folder and routing with examples.</p>
<h3 id="heading-tuomo-kankaanpaa">Tuomo Kankaanpaa</h3>
<div class="embed-wrapper">
        <iframe width="560" height="315" src="https://www.youtube.com/embed/xXwxEudjiAY" style="aspect-ratio: 16 / 9; width: 100%; height: auto;" title="YouTube video player" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen="" loading="lazy"></iframe></div>
<p>Tuomo Kankaanpaa teaches you about Next app folder routing, layouts, and server components. </p>
<h3 id="heading-piyush-garghttpswwwyoutubecomwatchvcbfbzvdqlis"><a target="_blank" href="https://www.youtube.com/watch?v=CBfBZvDQLis">Piyush Garg</a></h3>
<div class="embed-wrapper">
        <iframe width="560" height="315" src="https://www.youtube.com/embed/CBfBZvDQLis" style="aspect-ratio: 16 / 9; width: 100%; height: auto;" title="YouTube video player" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen="" loading="lazy"></iframe></div>
<p>Piyush Garg compiles all the new Next features and converts them into a small crash course, and builds a demo project.</p>
<p>Now that you're ready to go, let's get into building our blog.</p>
<h2 id="heading-folder-structure">Folder Structure</h2>
<p>Our folder structure looks like this for our demo application:</p>
<pre><code class="lang-bash">.
├── next.config.js
├── next-env.d.ts
├── package.json
├── pnpm-lock.yaml
├── postcss.config.js
├── public
├── README.md
├── search.json
├── src
│   └── app
│       ├── authors
│       │   └── [slug]
│       │       └── page.tsx
│       ├── BlogLayout.tsx
│       ├── cards.min.css
│       ├── Card.tsx
│       ├── error.tsx
│       ├── favicon.ico
│       ├── Footer.tsx
│       ├── ghost-client.ts
│       ├── globals.css
│       ├── Header.tsx
│       ├── layout.tsx
│       ├── not-found.tsx
│       ├── pages
│       │   └── [slug]
│       │       └── page.tsx
│       ├── page.tsx
│       ├── pagination
│       │   └── [item]
│       │       └── page.tsx
│       ├── Pagination.tsx
│       ├── <span class="hljs-built_in">read</span>
│       │   └── [slug]
│       │       ├── Newsletter.tsx
│       │       └── page.tsx
│       ├── Search.tsx
│       ├── SocialIcons.tsx
│       └── tags
│           └── [slug]
│               └── page.tsx
├── tailwind.config.js
└── tsconfig.json

13 directories, 30 files
</code></pre>
<h2 id="heading-how-to-configure-ghost-cms-and-nextjs">How to Configure Ghost CMS and Next.js</h2>
<p>The next step is to set up data fetching for the Ghost Content API. This is why we installed the <a target="_blank" href="https://www.npmjs.com/package/@tryghost/content-api">@tryghost/content-api</a> package above.  </p>
<p>Ghost CMS comes with two types of APIs: the first is the <a target="_blank" href="https://ghost.org/docs/content-api/">Content API</a>, and the second is the <a target="_blank" href="https://ghost.org/docs/admin-api/">Admin API</a>. For the blog, we'll use the <strong><a target="_blank" href="https://ghost.org/docs/content-api/">Content API</a>.</strong></p>
<p>Content API is a RESTful API that fetches the published content for the Ghost database. It is a read-only API. You can not call POST requests with it. </p>
<p>To configure it, we create a new file inside the <code>src/app</code> folder with <code>ghost-client.ts</code>. Inside the file, we have a new Ghost API instance. </p>
<pre><code class="lang-typescript"><span class="hljs-comment">// ghost-client.ts</span>

<span class="hljs-keyword">import</span> GhostContentAPI <span class="hljs-keyword">from</span> <span class="hljs-string">"@tryghost/content-api"</span>;

<span class="hljs-comment">// Create API instance with site credentials</span>
<span class="hljs-keyword">const</span> api = <span class="hljs-keyword">new</span> GhostContentAPI({
  url: process.env.GHOST_URL <span class="hljs-keyword">as</span> <span class="hljs-built_in">string</span>,
  key: process.env.GHOST_KEY <span class="hljs-keyword">as</span> <span class="hljs-built_in">string</span>,
  version: <span class="hljs-string">"v5.0"</span>
});
</code></pre>
<p>We need the blog URL, key, and version to config the Ghost content API in Next. You can find both the URLs and Key properties in the Ghost dashboard, as well as the version value which is your current version of Ghost CMS.</p>
<p>Go to the Ghost dashboard:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/03/ghost-next.gif" alt="Get your KEY and URL" width="600" height="400" loading="lazy">
<em>Get your KEY and URL</em></p>
<p>Go to <code>dashboard</code> &gt; <code>settings</code> &gt; <code>integrations</code> &gt; <code>Your-intergration-id</code> and get your <code>GHOST_URL</code> and <code>GHOST_KEY</code> . Now you can copy both and paste them inside your <code>.env.local</code> file.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/03/next-and-ghost.png" alt="Get your Ghost_Key and Ghost_URL" width="600" height="400" loading="lazy">
_Get your <code>GHOST_KEY</code> and <code>GHOST_URL</code>_</p>
<h2 id="heading-understanding-the-nextjs-13-app-folder">Understanding the Next.js 13 App Folder</h2>
<p>There have been lots of changes in the Next.js <code>pages</code> folder and <code>app</code> folder with the release of Next.js 13. We'll discuss some important stuff now and more when we're building the app:</p>
<ol>
<li>There is no <code>_app</code> , <code>_document</code>, <code>getServerSideProps</code>, <code>getStaticProps</code>, <code>getStaticPaths</code> , <code>404</code> and <code>useRouter</code>.</li>
<li>Now it combines the <code>_app</code> and <code>_document</code> files with the <code>layout</code> file.</li>
<li><code>useRouter</code> is import from <code>next/navigation</code>.</li>
<li>The <code>404</code> file is replaced by the <code>notFound()</code> function. </li>
<li>The <code>error.tsx</code> file provides functionality like reacting to error boundaries.</li>
<li>Now the <code>index.js</code> file is replaced by <code>page.js</code>.</li>
<li>Passing dynamic route segments <code>pages/blog/[slug].js</code> is changed, and the Next app directory looks like this: <code>app/blog/[slug]/page.js</code>.</li>
</ol>
<h3 id="heading-examples">Examples</h3>
<p>To understand the Next experimental app folder, let's look at a real example:</p>
<ol>
<li><strong>tag page</strong> =&gt; <code>app/tag/[slug]/page.ts</code></li>
<li><strong>category</strong> =&gt; <code>app/tag/[slug]/page.ts</code></li>
</ol>
<p>Now you can create five files inside every route. For example, if you create a <code>tag</code> or <strong><code>category</code></strong> route in your app folder, then you can create four files inside your app route folder.</p>
<ul>
<li><code>page.ts</code> (required): it is your main file.</li>
<li><code>layout.ts</code> (optional): it helps design your layout</li>
<li><code>loading.ts</code> (optional): it creates a loading indicator with React suspense. </li>
<li><code>error.ts</code> (optional): it helps handle errors in your React app.</li>
<li><code>components</code> (optional): you can also create another component in your routes.</li>
</ul>
<p>Let's understand how the new Next.js 13 app route works with a real-life example: your tag route folder looks like this.</p>
<pre><code class="lang-typescript">app/tag/[slug]/page.ts
app/tag/[slug]/loading.ts
app/tag/[slug]/layout.ts
app/tag/[slug]/error.ts
app/tag/[slug]/my-card-component.ts
</code></pre>
<h2 id="heading-demo-data-for-the-project">Demo Data for the Project</h2>
<p>You don't have to worry about creating a demo or dummy blog post data. For your testing, You can download it from this <a target="_blank" href="https://github.com/officialrajdeepsingh/nextjsghostcms/blob/main/.github/demo-post-for-ghost.json">GitHub repository</a>.</p>
<h2 id="heading-how-to-build-the-blog">How to Build the Blog</h2>
<p>We'll go through and build each part of the blog in the following sections so you can follow along at home.</p>
<ol>
<li><a class="post-section-overview" href="#heading-how-to-build-the-header">How to build the header</a></li>
<li><a class="post-section-overview" href="#heading-how-to-build-the-footer">How to build the footer</a></li>
<li><a class="post-section-overview" href="#heading-how-to-build-the-layout">How to build the layout</a></li>
<li><a class="post-section-overview" href="#how-to-built-the-homepage">How to build the homepage</a></li>
<li><a class="post-section-overview" href="#heading-how-to-build-the-reading-page">How to build the reading page</a></li>
<li><a class="post-section-overview" href="#heading-how-to-build-the-tag-page">How to build the tag page</a></li>
<li><a class="post-section-overview" href="#heading-how-to-build-the-author-page">How to build the author page</a></li>
<li><a class="post-section-overview" href="#heading-how-to-build-single-pages">How to build single pages</a></li>
<li><a class="post-section-overview" href="#heading-how-to-handle-pagination">How to handle pagination</a></li>
<li><a class="post-section-overview" href="#heading-nextjs-seo">Next.js SEO</a></li>
<li><a class="post-section-overview" href="#heading-how-to-enable-search">How to Enable Search</a></li>
<li><a class="post-section-overview" href="#heading-error-handling">Error Handling</a></li>
<li><a class="post-section-overview" href="#heading-how-to-rebuild-your-static-site-with-webhooks">How to rebuild your static site with webhooks</a></li>
</ol>
<h3 id="heading-how-to-build-the-header">How to Build the Header</h3>
<p>The first and main part of the site is the header. First, we'll create a simple header for our demo blog. Our header will end up looking like this:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/04/header.png" alt="Header of site" width="600" height="400" loading="lazy">
<em>Design of the header</em></p>
<p>First is the logo, next comes the nav bar with various elements, and last is the icon section. All the data comes from the Ghost CMS API. You can change things inside Ghost CMS and it will reflect on the site.</p>
<p>Here's the code to build the header component:</p>
<pre><code class="lang-typescript"><span class="hljs-comment">// Header.tsx</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> SocialIcons <span class="hljs-keyword">from</span> <span class="hljs-string">"./SocialIcons"</span>;
<span class="hljs-keyword">import</span> Image <span class="hljs-keyword">from</span> <span class="hljs-string">"next/image"</span>;
<span class="hljs-keyword">import</span> <span class="hljs-keyword">type</span> { Settings } <span class="hljs-keyword">from</span> <span class="hljs-string">"@tryghost/content-api"</span>;

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">Header</span>(<span class="hljs-params">{ setting }: { setting: Settings }</span>) </span>{

  <span class="hljs-keyword">return</span> (
    &lt;header className=<span class="hljs-string">"px-2 sm:px-4 py-2.5 dark:bg-gray-900 w-full"</span>&gt;

      &lt;div className=<span class="hljs-string">"container flex flex-wrap items-center justify-between mx-auto"</span>&gt;
        {<span class="hljs-comment">/* Logo for blog */</span>}
        &lt;Link href=<span class="hljs-string">"/"</span> className=<span class="hljs-string">"flex items-center"</span>&gt;
          {setting.logo !== <span class="hljs-literal">null</span> ?
            &lt;Image
              alt={setting.title} width={<span class="hljs-number">200</span>} height={<span class="hljs-number">100</span>} src={setting.logo} className=<span class="hljs-string">"self-center text-xl font-semibold whitespace-nowrap dark:text-white"</span> /&gt;
            : setting.title}
        &lt;/Link&gt;
        &lt;div className=<span class="hljs-string">"flex md:order-2"</span>&gt;

          &lt;ul className=<span class="hljs-string">"flex flex-wrap p-4 md:space-x-8 md:mt-0 md:text-sm md:font-medium"</span>&gt;

            {
              <span class="hljs-comment">/* Blog Navigation Edit in GHOST CMS  */</span>
              setting.navigation !== <span class="hljs-literal">undefined</span> ? setting?.navigation.map(<span class="hljs-function"><span class="hljs-params">item</span> =&gt;</span> &lt;li key={item.label} className=<span class="hljs-string">"block py-2 pl-3 pr-4 text-gray-700 rounded hover:text-blue-700 dark:hover:text-blue-700 md:p-0 dark:text-white"</span>
                aria-current=<span class="hljs-string">"page"</span>&gt;
                &lt;Link href={item.url}&gt;
                  {item.label}
                &lt;/Link&gt;
              &lt;/li&gt;) : <span class="hljs-string">" "</span>

            }

          &lt;/ul&gt;

        &lt;/div&gt;
        &lt;SocialIcons setting={setting} /&gt;
      &lt;/div&gt;

    &lt;/header &gt;
  )

}
<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> Header
</code></pre>
<h3 id="heading-how-to-build-the-footer">How to Build the Footer</h3>
<p>The footer is also an important section of a blog site. It shows your important information and various helpful links. </p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/04/footer.png" alt="Design footer" width="600" height="400" loading="lazy">
<em>Design of the footer</em></p>
<p>I designed a basic footer with copyrighted text and added social icons for the site. The social icons come from the Ghost CMS API.</p>
<pre><code class="lang-typescript"><span class="hljs-comment">// Footer.tsx</span>

<span class="hljs-keyword">import</span> { FaTwitter, FaFacebook } <span class="hljs-keyword">from</span> <span class="hljs-string">"react-icons/fa"</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> <span class="hljs-keyword">type</span> { Settings } <span class="hljs-keyword">from</span> <span class="hljs-string">"@tryghost/content-api"</span>;

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">Footer</span>(<span class="hljs-params">{ setting }: { setting: Settings }</span>) </span>{

  <span class="hljs-keyword">return</span> (

    &lt;footer className=<span class="hljs-string">"px-2 sm:px-4 py-2.5 dark:bg-gray-900 w-full"</span>&gt;

      &lt;div className=<span class="hljs-string">"container flex flex-wrap items-center justify-between mx-auto"</span>&gt;

        &lt;Link href=<span class="hljs-string">"https://github.com/frontendweb3"</span> className=<span class="hljs-string">"flex items-center"</span>&gt;
          &lt;span className=<span class="hljs-string">"self-center text-gray-800 text-sm font-semibold whitespace-nowrap dark:text-white"</span>&gt;<span class="hljs-number">2023</span> copyright frontend web&lt;/span&gt;
        &lt;/Link&gt;

        &lt;div className=<span class="hljs-string">"flex md:order-2"</span>&gt;

          &lt;ul className=<span class="hljs-string">"flex p-4 flex-row md:space-x-8 md:mt-0 md:text-sm font-medium"</span>&gt;

            {
              setting.twitter !== <span class="hljs-literal">null</span> ? &lt;li&gt;
                &lt;Link target=<span class="hljs-string">"_blank"</span> href={<span class="hljs-string">`https://twitter.com/<span class="hljs-subst">${setting.twitter}</span>`</span>} className=<span class="hljs-string">"block py-2 pl-3 pr-4 text-gray-700 rounded hover:text-blue-700 dark:hover:text-blue-700 md:p-0 dark:text-white"</span> aria-current=<span class="hljs-string">"page"</span>&gt;
                  &lt;FaTwitter /&gt;
                &lt;/Link&gt;
              &lt;/li&gt; : <span class="hljs-string">" "</span>

            }

            {
              setting.facebook !== <span class="hljs-literal">null</span> ? &lt;li&gt;
                &lt;Link target=<span class="hljs-string">"_blank"</span> href={<span class="hljs-string">`https://www.facebook.com/<span class="hljs-subst">${setting.facebook}</span>`</span>} className=<span class="hljs-string">"block py-2 pl-3 pr-4 text-gray-700 rounded hover:text-blue-700 dark:hover:text-blue-700 md:p-0 dark:text-white "</span>&gt;
                  &lt;FaFacebook /&gt;
                &lt;/Link&gt;
              &lt;/li&gt; : <span class="hljs-string">" "</span>

            }

          &lt;/ul&gt;
        &lt;/div&gt;

      &lt;/div&gt;
    &lt;/footer&gt;

  )
}

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> Footer
</code></pre>
<h3 id="heading-how-to-build-the-layout">How to Build the Layout</h3>
<p>I designed a basic layout for the blog. For building layouts in Next.js, there's a special <code>layout.tsx</code> file.</p>
<p>Before we create the layout design, we need to define a <code>getNavigation</code> function to <strong>fetch</strong> navigation and basic website-related data from Ghost. </p>
<pre><code class="lang-typescript"><span class="hljs-comment">// ghost-client.ts</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">getNavigation</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">return</span> <span class="hljs-keyword">await</span> api.settings.browse()
}
</code></pre>
<h4 id="heading-the-data-look-like-this">The data look like this:</h4>
<pre><code class="lang-object">{
  title: 'Rajdeep Singh',
  description: 'Thoughts, stories and ideas.',
  logo: 'http://localhost:2368/content/images/2023/04/nextjsandghostlogo-2.png',
  icon: 'http://localhost:2368/content/images/size/w256h256/2023/04/nextjs-60pxx60px.png',
  accent_color: '#d27fa0',
  cover_image: 'https://static.ghost.org/v4.0.0/images/publication-cover.jpg',
  facebook: 'ghost',
  twitter: '@ghost',
  lang: 'en',
  locale: 'en',
  timezone: 'Etc/UTC',
  codeinjection_head: null,
  codeinjection_foot: null,
  navigation: Array(5) [
    { label: 'Home', url: '/' }, { label: 'JavaScript', url: '/tags/javascript/' }, { label: 'Nextjs', url: '/tags/nextjs/' },
    { label: 'Reactjs', url: '/tags/reactjs/' }, { label: 'Ghost CMS', url: '/tags/ghost-cms/' }
  ],
  secondary_navigation: Array(1) [ { label: 'Login', url: '#/portal/' } ],
  meta_title: 'My demo post',
  meta_description: 
    'Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry\'s standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book.',
  og_image: null,
  og_title: null,
  og_description: 
    'Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry\'s standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book.',
  twitter_image: null,
  twitter_title: null,
  twitter_description: 
    'Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry\'s standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book.',
  members_support_address: 'noreply',
  members_enabled: true,
  members_invite_only: false,
  paid_members_enabled: false,
  firstpromoter_account: null,
  portal_button_style: 'icon-and-text',
  portal_button_signup_text: 'Subscribe',
  portal_button_icon: null,
  portal_plans: Array(1) [ 'free' ],
  portal_name: true,
  portal_button: true,
  comments_enabled: 'all',
  url: 'http://localhost:2368/',
  version: '5.39'
}
</code></pre>
<p>The <code>getNavigation</code> function returns the settings data, and then we pass the data as props into the header and footer components.</p>
<p>Our Main <code>layout.tsx</code> file works server side. It helps fetch data on the server side with the React <code>use</code> hook.</p>
<pre><code class="lang-typescript"><span class="hljs-comment">// Layout.tsx</span>


<span class="hljs-keyword">import</span> <span class="hljs-string">"./globals.css"</span>;
<span class="hljs-keyword">import</span> BlogLayout <span class="hljs-keyword">from</span> <span class="hljs-string">'./BlogLayout'</span>
<span class="hljs-keyword">import</span> { getNavigation, } <span class="hljs-keyword">from</span> <span class="hljs-string">"./ghost-client"</span>
<span class="hljs-keyword">import</span> { use } <span class="hljs-keyword">from</span> <span class="hljs-string">"react"</span>
<span class="hljs-keyword">import</span> <span class="hljs-keyword">type</span> { Settings } <span class="hljs-keyword">from</span> <span class="hljs-string">"@tryghost/content-api"</span>

<span class="hljs-keyword">interface</span> UpdateSettings <span class="hljs-keyword">extends</span> Settings {
  accent_color?: <span class="hljs-built_in">string</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">RootLayout</span>(<span class="hljs-params">{
  children,
}: {
  children: React.ReactNode
}</span>) </span>{

  <span class="hljs-keyword">const</span> settings: UpdateSettings = use(getNavigation())

  <span class="hljs-keyword">return</span> (

    &lt;html className=<span class="hljs-string">'light'</span> lang=<span class="hljs-string">"en"</span>&gt;

      &lt;body
        style={{
          <span class="hljs-string">'--bg-color'</span>: settings?.accent_color ? settings.accent_color : <span class="hljs-string">""</span>,
        }}
        className={<span class="hljs-string">` bg-[--bg-color] dark:bg-gray-900`</span>}&gt;

        &lt;BlogLayout setting={settings}&gt;

          {children}

        &lt;/BlogLayout&gt;

      &lt;/body&gt;

    &lt;/html&gt;

  )
}
</code></pre>
<h4 id="heading-bloglayout-component">BlogLayout component</h4>
<p>The <code>BlogLayout</code> component works on the client side. In the Next.js app folder, you can easily convert your server-side component to the client side with the following <code>"use client"</code> syntax.</p>
<p>The purpose of the BlogLayout component is to contain the <a target="_blank" href="https://www.npmjs.com/package/next-themes">ThemeProvider</a>, header, and footer. ThemeProvider is a high-order component, and it provides additional functionality, like changing the theme from dark to light. We wrap the intra-site with ThemeProvider's higher component. In the old pages directory, we achieve similarly functionally with  nextjs <code>_app.ts</code> custom app.</p>
<p>ThemeProvider component helps to change the theme from light to dark mode.</p>
<pre><code class="lang-typescript"><span class="hljs-string">"use client"</span>

<span class="hljs-comment">// BlogLayout.tsx</span>

<span class="hljs-keyword">import</span> Footer <span class="hljs-keyword">from</span> <span class="hljs-string">"./Footer"</span>;
<span class="hljs-keyword">import</span> Header <span class="hljs-keyword">from</span> <span class="hljs-string">"./Header"</span>;
<span class="hljs-keyword">import</span> { ThemeProvider } <span class="hljs-keyword">from</span> <span class="hljs-string">'next-themes'</span>;
<span class="hljs-keyword">import</span> <span class="hljs-keyword">type</span> { Settings } <span class="hljs-keyword">from</span> <span class="hljs-string">"@tryghost/content-api"</span>;
<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">Layout</span>(<span class="hljs-params">{ setting, children }: { setting: Settings, children: React.ReactNode }</span>) </span>{
  <span class="hljs-keyword">return</span> &lt;ThemeProvider attribute=<span class="hljs-string">"class"</span>&gt;
    &lt;Header setting={setting} /&gt;
    {children}
    &lt;Footer setting={setting} /&gt;
  &lt;/ThemeProvider&gt;

}
<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> Layout
</code></pre>
<h3 id="heading-how-to-build-the-homepage">How to Build the Homepage</h3>
<p>Next.js has a special <code>app/page.tsx</code> file for designing and building the home page. Our blog website's home page looks like what you see below. We import the header, card, pagination, and footer on the home page. The header and footer are part of <code>layout.tsx</code>.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/04/Home-page-1.png" alt="Home page" width="600" height="400" loading="lazy">
<em>Home page</em></p>
<p>First, we fetch all posts data from Ghost CMS with the help of the <code>getPosts</code>  function, which I defined in the <code>ghost-client.ts</code> file. </p>
<pre><code class="lang-typescript"><span class="hljs-comment">// ghost-client.ts</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">getPosts</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">return</span> <span class="hljs-keyword">await</span> api.posts
    .browse({
      include: [<span class="hljs-string">"tags"</span>, <span class="hljs-string">"authors"</span>],
      limit: <span class="hljs-number">10</span>
    })
    .catch(<span class="hljs-function"><span class="hljs-params">err</span> =&gt;</span> {
      <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> <span class="hljs-built_in">Error</span>(err)
    });
}
</code></pre>
<p>By default, the <code>api.post.browse()</code> returns only post data, but you can easily extend it. In every article or post data, we also include tags and authors with the help of <code>include</code>. Then we set the article limit to ten.</p>
<h4 id="heading-the-data-look-like-this-1">The data look like this:</h4>
<pre><code class="lang-json"> [
  {
    id: '<span class="hljs-number">6422</span>a742136f5d40f37294f5',
    uuid: '<span class="hljs-number">8</span>c2fcfda-a6e4<span class="hljs-number">-4383</span><span class="hljs-number">-893</span>b-ba18511c0f67',
    title: 'Demo Posts with Nextjs and Ghost Editor',
    slug: 'demo-posts-with-nextjs-and-reactjs',
    html: `&lt;p&gt;&lt;strong&gt;Lorem Ipsum&lt;/strong&gt; is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text si
nce the <span class="hljs-number">1500</span>s when an unknown printer scrambled a galley of type and scrambled it to make a type specimen book. &lt;/p&gt;&lt;p&gt;It has survived five centuries and the leap i
nto electronic typesetting, remaining essentially unchanged. &lt;/p&gt;&lt;p&gt;It was popularised in the 1960s with Letraset sheets containing Lorem Ipsum passages and, more r
ecently, desktop publishing software like Aldus PageMaker, including versions of Lorem Ipsum.&lt;/p&gt;&lt;figure class=<span class="hljs-attr">"kg-card kg-gallery-card kg-width-wide kg-card-hascap
tion"</span>&gt;&lt;div class=<span class="hljs-attr">"kg-gallery-container"</span>&gt;&lt;div class=<span class="hljs-attr">"kg-gallery-row"</span>&gt;&lt;div class=<span class="hljs-attr">"kg-gallery-image"</span>&gt;&lt;img src=<span class="hljs-attr">"http://localhost:2368/content/images/2023/03/Build-and-d
eploy.png"</span> width=<span class="hljs-attr">"1500"</span> height=<span class="hljs-attr">"400"</span> loading=<span class="hljs-attr">"lazy"</span> alt srcset=<span class="hljs-attr">"http://localhost:2368/content/images/size/w600/2023/03/Build-and-deploy.png 600w, http://localhost:2
368/content/images/size/w1000/2023/03/Build-and-deploy.png 1000w, http://localhost:2368/content/images/2023/03/Build-and-deploy.png 1500w"</span> sizes=<span class="hljs-attr">"(min-width: 720px)
 720px"</span>&gt;&lt;/div&gt;&lt;div class=<span class="hljs-attr">"kg-gallery-image"</span>&gt;&lt;img src=<span class="hljs-attr">"http://localhost:2368/content/images/2023/03/Build-and-deploy-profile-1.png"</span> width=<span class="hljs-attr">"1500"</span> height=<span class="hljs-attr">"400"</span> loading
=<span class="hljs-attr">"lazy"</span> alt srcset=<span class="hljs-attr">"http://localhost:2368/content/images/size/w600/2023/03/Build-and-deploy-profile-1.png 600w, http://localhost:2368/content/images/size/w1000/2023
/03/Build-and-deploy-profile-1.png 1000w, http://localhost:2368/content/images/2023/03/Build-and-deploy-profile-1.png 1500w"</span> sizes=<span class="hljs-attr">"(min-width: 720px) 720px"</span>&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class=<span class="hljs-attr">"kg-gallery-row"</span>&gt;&lt;div class=<span class="hljs-attr">"kg-gallery-image"</span>&gt;&lt;img src=<span class="hljs-attr">"http://localhost:2368/content/images/2023/03/Build-and-deploy-profile--1--1.png"</span> width=<span class="hljs-attr">"15
00"</span> height=<span class="hljs-attr">"400"</span> loading=<span class="hljs-attr">"lazy"</span> alt srcset=<span class="hljs-attr">"http://localhost:2368/content/images/size/w600/2023/03/Build-and-deploy-profile--1--1.png 600w, http://localhost:2368/co
ntent/images/size/w1000/2023/03/Build-and-deploy-profile--1--1.png 1000w, http://localhost:2368/content/images/2023/03/Build-and-deploy-profile--1--1.png 1500w"</span> siz
es=<span class="hljs-attr">"(min-width: 720px) 720px"</span>&gt;&lt;/div&gt;&lt;div class=<span class="hljs-attr">"kg-gallery-image"</span>&gt;&lt;img src=<span class="hljs-attr">"http://localhost:2368/content/images/2023/03/Build--Test-and-Deploy-profile-1.png"</span> width
=<span class="hljs-attr">"1500"</span> height=<span class="hljs-attr">"400"</span> loading=<span class="hljs-attr">"lazy"</span> alt srcset=<span class="hljs-attr">"http://localhost:2368/content/images/size/w600/2023/03/Build--Test-and-Deploy-profile-1.png 600w, http://localhost:2
368/content/images/size/w1000/2023/03/Build--Test-and-Deploy-profile-1.png 1000w, http://localhost:2368/content/images/2023/03/Build--Test-and-Deploy-profile-1.png 
1500w"</span> sizes=<span class="hljs-attr">"(min-width: 720px) 720px"</span>&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;figcaption&gt;Build and deploy&lt;/figcaption&gt;&lt;/figure&gt;&lt;h2 id=<span class="hljs-attr">"why-do-we-use-it"</span>&gt;Why do we use it?&lt;/h2&gt;&lt;p&gt;It is
 a long-established fact that a reader will be distracted by the readable content of a page when looking at its layout. &lt;/p&gt;&lt;p&gt;The point of using Lorem Ipsum is tha
t it has a more-or-less normal distribution of letters, as opposed to using 'Content here, content here', making it look like readable English. &lt;/p&gt;&lt;p&gt;Many desktop 
publishing packages and web page editors now use Lorem Ipsum as their default model text, and a search for 'lorem ipsum' will uncover many web sites still in their 
infancy. &lt;/p&gt;&lt;p&gt;Various versions have evolved over the years, sometimes by accident, sometimes on purpose (injected humour and the like).&lt;/p&gt;&lt;hr&gt;&lt;h2 id=<span class="hljs-attr">"where-can-i
-get-some"</span>&gt;Where can I get some?&lt;/h2&gt;&lt;p&gt;There are many variations of passages of Lorem Ipsum available, but the majority have suffered alteration in some form, by i
njected humour, or randomised words which don't look even slightly believable. &lt;/p&gt;&lt;p&gt;If you are going to use a passage of Lorem Ipsum, you need to be sure there is
n't anything embarrassing hidden in the middle of text. &lt;/p&gt;&lt;p&gt;All the Lorem Ipsum generators on the Internet tend to repeat predefined chunks as necessary, making 
this the first true generator on the Internet. &lt;/p&gt;&lt;p&gt;It uses a dictionary of over 200 Latin words, combined with a handful of model sentence structures, to generat
e Lorem Ipsum which looks reasonable. &lt;/p&gt;&lt;p&gt;The generated Lorem Ipsum is therefore always free from repetition, injected humour, or non-characteristic words etc.&lt;/
p&gt;&lt;div class=<span class="hljs-attr">"kg-card kg-callout-card kg-callout-card-red"</span>&gt;&lt;div class=<span class="hljs-attr">"kg-callout-emoji"</span>&gt;💡&lt;/div&gt;&lt;div class=<span class="hljs-attr">"kg-callout-text"</span>&gt;My note is here&amp;nbsp;&lt;/div&gt;&lt;/div&gt;&lt;p&gt;&lt;/
p&gt;&lt;div class=<span class="hljs-attr">"kg-card kg-header-card kg-width-full kg-size-small kg-style-dark"</span> style data-kg-background-image&gt;&lt;h2 class=<span class="hljs-attr">"kg-header-card-header"</span> id=<span class="hljs-attr">"product"</span>&gt;Produc
t&lt;/h2&gt;&lt;h3 class=<span class="hljs-attr">"kg-header-card-subheader"</span> id=<span class="hljs-attr">"my-blog-list"</span>&gt;My blog list&lt;/h3&gt;&lt;/div&gt;&lt;p&gt;&lt;/p&gt;&lt;figure class=<span class="hljs-attr">"kg-card kg-embed-card kg-card-hascaption"</span>&gt;&lt;iframe width=<span class="hljs-attr">"2
00"</span> height=<span class="hljs-attr">"113"</span> src=<span class="hljs-attr">"https://www.youtube.com/embed/_q1K7cybyRk?feature=oembed"</span> frameborder=<span class="hljs-attr">"0"</span> allow=<span class="hljs-attr">"accelerometer; autoplay; clipboard-write; encrypted-media; gy
roscope; picture-in-picture; web-share"</span> allowfullscreen title=<span class="hljs-attr">"Next.js 13.1 Explained!"</span>&gt;&lt;/iframe&gt;&lt;figcaption&gt;youtube&lt;/figcaption&gt;&lt;/figure&gt;&lt;hr&gt;&lt;figure class=<span class="hljs-attr">"kg-card
 kg-embed-card"</span>&gt;&lt;blockquote class=<span class="hljs-attr">"twitter-tweet"</span>&gt;&lt;p lang=<span class="hljs-attr">"en"</span> dir=<span class="hljs-attr">"ltr"</span>&gt;In 2022, we enabled developers to create at the moment of inspiration, now with over 2 mill
ion deployments per week.&lt;br&gt;&lt;br&gt;Here&amp;#39;s what we shipped ↓ &lt;a href=<span class="hljs-attr">"https://t.co/6k7Xmbpna3?ref=localhost"</span>&gt;pic.twitter.com/6k7Xmbpna3&lt;/a&gt;&lt;/p&gt;&amp;mdash; Vercel (@ver
cel) &lt;a href=<span class="hljs-attr">"https://twitter.com/vercel/status/1611094825587167254?ref_src=twsrc%5Etfw&amp;ref=localhost"</span>&gt;January 5, 2023&lt;/a&gt;&lt;/blockquote&gt;\n` +
      '&lt;script async src=<span class="hljs-attr">"https://platform.twitter.com/widgets.js"</span> charset=<span class="hljs-attr">"utf-8"</span>&gt;&lt;/script&gt;\n' +
      '&lt;/figure&gt;&lt;hr&gt;&lt;figure class=<span class="hljs-attr">"kg-card kg-bookmark-card kg-card-hascaption"</span>&gt;&lt;a class=<span class="hljs-attr">"kg-bookmark-container"</span> href=<span class="hljs-attr">"https://medium.com/frontendweb/what-is-progre
ssive-web-app-and-how-to-enable-it-in-nextjs-application-17f2e3240390?ref=localhost"</span>&gt;&lt;div class=<span class="hljs-attr">"kg-bookmark-content"</span>&gt;&lt;div class=<span class="hljs-attr">"kg-bookmark-title"</span>&gt;What is Progres
sive Web App and How to enable it in nextjs Application?&lt;/div&gt;&lt;div class=<span class="hljs-attr">"kg-bookmark-description"</span>&gt;A detailed guide to Progressive Web Apps: How to use it with next
js and publish on Google play store, Microsoft store, Meta Quest, and…&lt;/div&gt;&lt;div class=<span class="hljs-attr">"kg-bookmark-metadata"</span>&gt;&lt;img class=<span class="hljs-attr">"kg-bookmark-icon"</span> src=<span class="hljs-attr">"https://cdn-static-
1.medium.com/_/fp/icons/Medium-Avatar-500x500.svg"</span> alt&gt;&lt;span class=<span class="hljs-attr">"kg-bookmark-author"</span>&gt;FrontEnd web&lt;/span&gt;&lt;span class=<span class="hljs-attr">"kg-bookmark-publisher"</span>&gt;Rajdeep singh&lt;/span&gt;&lt;
/div&gt;&lt;/div&gt;&lt;div class=<span class="hljs-attr">"kg-bookmark-thumbnail"</span>&gt;&lt;img src=<span class="hljs-attr">"https://miro.medium.com/v2/resize:fit:1200/1*yAoHfq4Wm2Bp8DU1Dav29Q.png"</span> alt&gt;&lt;/div&gt;&lt;/a&gt;&lt;figcaption&gt;Bookmark&lt;
/figcaption&gt;&lt;/figure&gt;&lt;div class=<span class="hljs-attr">"kg-card kg-header-card kg-width-full kg-size-small kg-style-dark"</span> style data-kg-background-image&gt;&lt;h2 class=<span class="hljs-attr">"kg-header-card-header"</span> 
id=<span class="hljs-attr">"thank-you"</span>&gt;Thank you&lt;/h2&gt;&lt;/div&gt;',
    comment_id: '<span class="hljs-number">6422</span>a742136f5d40f37294f5',
    feature_image: 'https:<span class="hljs-comment">//images.unsplash.com/photo-1543966888-7c1dc482a810?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=MnwxMTc3M3wwfDF8c2VhcmNofDE2fHxqYXZhc2Nya</span>
XB0fGVufDB8fHx8MTY3OTk5MjY1NA&amp;ixlib=rb<span class="hljs-number">-4.0</span><span class="hljs-number">.3</span>&amp;q=<span class="hljs-number">80</span>&amp;w=<span class="hljs-number">2000</span>',
    featured: <span class="hljs-literal">false</span>,
    visibility: 'public',
    created_at: '<span class="hljs-number">2023</span><span class="hljs-number">-03</span><span class="hljs-number">-28</span>T08:<span class="hljs-number">37</span>:<span class="hljs-number">22.000</span>+<span class="hljs-number">00</span>:<span class="hljs-number">00</span>',
    updated_at: '<span class="hljs-number">2023</span><span class="hljs-number">-03</span><span class="hljs-number">-28</span>T08:<span class="hljs-number">51</span>:<span class="hljs-number">38.000</span>+<span class="hljs-number">00</span>:<span class="hljs-number">00</span>',
    published_at: '<span class="hljs-number">2023</span><span class="hljs-number">-03</span><span class="hljs-number">-28</span>T08:<span class="hljs-number">50</span>:<span class="hljs-number">44.000</span>+<span class="hljs-number">00</span>:<span class="hljs-number">00</span>',
    custom_excerpt: 'It has survived five centuries and the leap into electronic typesetting, remaining essentially unchanged. ',
    codeinjection_head: <span class="hljs-literal">null</span>,
    codeinjection_foot: <span class="hljs-literal">null</span>,
    custom_template: <span class="hljs-literal">null</span>,
    canonical_url: <span class="hljs-literal">null</span>,
    tags: [ [Object] ],
    authors: [ [Object] ],
    primary_author: {
      id: '<span class="hljs-number">1</span>',
      name: 'Rajdeep Singh',
      slug: 'rajdeep',
      profile_image: 'https:<span class="hljs-comment">//www.gravatar.com/avatar/dafca7497609ae294378279ad1d6136c?s=250&amp;r=x&amp;d=mp',</span>
      cover_image: <span class="hljs-literal">null</span>,
      bio: 'Lorem Ipsum is simply dummy text of the printing and typesetting industry. ',
      website: 'https:<span class="hljs-comment">//officialrajdeepsingh.dev',</span>
      location: 'India',
      facebook: 'officialrajdeepsingh',
      twitter: '@Official_R_deep',
      meta_title: <span class="hljs-literal">null</span>,
      meta_description: <span class="hljs-literal">null</span>,
      url: 'http:<span class="hljs-comment">//localhost:2368/author/rajdeep/'</span>
    },
    primary_tag: {
      id: '<span class="hljs-number">6422</span>aa9a136f5d40f3729552',
      name: 'demo',
      slug: 'demo',
      description: <span class="hljs-literal">null</span>,
      feature_image: <span class="hljs-literal">null</span>,
      visibility: 'public',
      og_image: <span class="hljs-literal">null</span>,
      og_title: <span class="hljs-literal">null</span>,
      og_description: <span class="hljs-literal">null</span>,
      twitter_image: <span class="hljs-literal">null</span>,
      twitter_title: <span class="hljs-literal">null</span>,
      twitter_description: <span class="hljs-literal">null</span>,
      meta_title: <span class="hljs-literal">null</span>,
      meta_description: <span class="hljs-literal">null</span>,
      codeinjection_head: <span class="hljs-literal">null</span>,
      codeinjection_foot: <span class="hljs-literal">null</span>,
      canonical_url: <span class="hljs-literal">null</span>,
      accent_color: <span class="hljs-literal">null</span>,
      url: 'http:<span class="hljs-comment">//localhost:2368/tag/demo/'</span>
    },
    url: 'http:<span class="hljs-comment">//localhost:2368/demo-posts-with-nextjs-and-reactjs/',</span>
    excerpt: 'It has survived five centuries and the leap into electronic typesetting, remaining essentially unchanged. ',
    reading_time: <span class="hljs-number">3</span>,
    access: <span class="hljs-literal">true</span>,
    comments: <span class="hljs-literal">true</span>,
    og_image: <span class="hljs-literal">null</span>,
    og_title: <span class="hljs-literal">null</span>,
    og_description: <span class="hljs-literal">null</span>,
    twitter_image: <span class="hljs-literal">null</span>,
    twitter_title: <span class="hljs-literal">null</span>,
    twitter_description: <span class="hljs-literal">null</span>,
    meta_title: <span class="hljs-literal">null</span>,
    meta_description: <span class="hljs-literal">null</span>,
    email_subject: <span class="hljs-literal">null</span>,
    frontmatter: <span class="hljs-literal">null</span>,
    feature_image_alt: 'Demo Posts with Nextjs and Ghost Editor',
    feature_image_caption: 'Photo by &lt;a href=<span class="hljs-string">"https://unsplash.com/@pinjasaur?utm_source=ghost&amp;utm_medium=referral&amp;utm_campaign=api-credit"</span>&gt;Paul Esch-Laurent&lt;/a&gt; / 
&lt;a href=<span class="hljs-string">"https://unsplash.com/?utm_source=ghost&amp;utm_medium=referral&amp;utm_campaign=api-credit"</span>&gt;Unsplash&lt;/a&gt;'
  },
meta:{
    pagination: { page: <span class="hljs-number">1</span>, limit: <span class="hljs-number">10</span>, pages: <span class="hljs-number">2</span>, total: <span class="hljs-number">12</span>, next: <span class="hljs-number">2</span>, prev: <span class="hljs-literal">null</span> }
  }
]
</code></pre>
<p>Now we call the <code>getPosts</code> function on the server side. It returns all the post data with the associated tags and authors. Now you can loop through the data with a <code>map()</code> function. </p>
<p>We pass the data into <code>app/page.tsx</code> to the <code>card.tsx</code> components. We pass the article data as props into the card component.</p>
<pre><code class="lang-typescript">
<span class="hljs-comment">// src/app/page.tsx</span>

<span class="hljs-keyword">import</span> { getPosts } <span class="hljs-keyword">from</span> <span class="hljs-string">"./ghost-client"</span>
<span class="hljs-keyword">import</span> Card <span class="hljs-keyword">from</span> <span class="hljs-string">'./Card'</span>

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> <span class="hljs-keyword">async</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> getPost = <span class="hljs-keyword">await</span> getPosts()

  <span class="hljs-keyword">return</span> (
    &lt;&gt;
      &lt;main className=<span class="hljs-string">"container my-12 mx-auto grid grid-cols-1 gap-2 md:gap-3 lg:gap-4 lg:grid-cols-3 md:grid-cols-2 xl:grid-cols-4 2xl:grid-cols-4"</span>&gt;

        {
          getPost?.map(
            <span class="hljs-function"><span class="hljs-params">item</span> =&gt;</span> {
              <span class="hljs-keyword">return</span> &lt;Card key={item.uuid} item={item} /&gt;
            })
        }

      &lt;/main&gt;

    &lt;/&gt;
  )
}
</code></pre>
<h4 id="heading-card-component">Card component</h4>
<p>I designed a basic card for the blog. The card component looks like this:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/04/card.png" alt="Card component" width="600" height="400" loading="lazy">
<em>Card component</em></p>
<p>I rendered every item of data coming from the home page as props and showed it on the site with <code>Card.tsx</code> .</p>
<pre><code class="lang-typescript"><span class="hljs-comment">// Card.tsx</span>

<span class="hljs-keyword">import</span> Image <span class="hljs-keyword">from</span> <span class="hljs-string">"next/image"</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> <span class="hljs-keyword">type</span> { PostOrPage } <span class="hljs-keyword">from</span> <span class="hljs-string">"@tryghost/content-api"</span>;
<span class="hljs-keyword">import</span> { format } <span class="hljs-keyword">from</span> <span class="hljs-string">'date-fns'</span>

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">Card</span>(<span class="hljs-params">{ item }: { item: PostOrPage }</span>) </span>{

  <span class="hljs-keyword">return</span> (
    &lt;div className=<span class="hljs-string">"max-w-full bg-white dark:bg-gray-800"</span> &gt;

      {
        item.featured !== <span class="hljs-literal">null</span> &amp;&amp; item.feature_image !== <span class="hljs-literal">undefined</span> ? &lt;Link href={<span class="hljs-string">`/read/<span class="hljs-subst">${item.slug}</span>`</span>}&gt;
          &lt;Image className=<span class="hljs-string">"rounded-lg p-3"</span> width={<span class="hljs-number">1000</span>} height={<span class="hljs-number">324</span>} src={item.feature_image} alt={item.feature_image_alt || item.title} /&gt;
        &lt;/Link&gt; : <span class="hljs-string">" "</span>
      }

      &lt;div className=<span class="hljs-string">"p-3"</span>&gt;

        &lt;div className=<span class="hljs-string">"flex mb-3"</span>&gt;
          {
            item.published_at !== <span class="hljs-literal">null</span> &amp;&amp; item.published_at !== <span class="hljs-literal">undefined</span> ? &lt;p className=<span class="hljs-string">"text-sm text-gray-500 dark:text-gray-400"</span>&gt;
              {format(<span class="hljs-keyword">new</span> <span class="hljs-built_in">Date</span>(item.published_at), <span class="hljs-string">'dd MMMM, yyyy'</span>)}
            &lt;/p&gt; : <span class="hljs-string">""</span>
          }
          &lt;p className=<span class="hljs-string">"text-sm text-gray-500 dark:text-gray-400 mx-1"</span>&gt; , &lt;/p&gt;
          &lt;p className=<span class="hljs-string">"text-sm text-gray-500 dark:text-gray-400"</span>&gt;
            {item.reading_time} min read
          &lt;/p&gt;
        &lt;/div&gt;

        &lt;Link href={<span class="hljs-string">`/read/<span class="hljs-subst">${item.slug}</span>`</span>}&gt;
          &lt;h5 className=<span class="hljs-string">"mb-2 text-2xl font-bold tracking-tight text-gray-900 dark:text-white"</span>&gt;
            {item.title}
          &lt;/h5&gt;
        &lt;/Link&gt;


      &lt;/div&gt;

    &lt;/div&gt;

  )

}



<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> Card
</code></pre>
<h3 id="heading-how-to-build-the-reading-page">How to Build the Reading Page</h3>
<p>The reading page is the second most important page for the blog site. If people can't figure out how to read what the author writes, this is a big problem for front-end developers. </p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/04/ghostandnext-reading.png" alt="Image" width="600" height="400" loading="lazy">
<em>Reading page</em></p>
<p>First, we get a single article from the Ghost CMS API based on its slug. We pass it to the <code>Card</code> component with the <code>Link</code> component.</p>
<pre><code class="lang-typescript"><span class="hljs-comment">// ghost-client.ts</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">getSinglePost</span>(<span class="hljs-params">postSlug: <span class="hljs-built_in">string</span></span>) </span>{
  <span class="hljs-keyword">return</span> <span class="hljs-keyword">await</span> api.posts
    .read({
      slug: postSlug
    }, { include: [<span class="hljs-string">"tags"</span>, <span class="hljs-string">"authors"</span>] })
    .catch(<span class="hljs-function"><span class="hljs-params">err</span> =&gt;</span> {
      <span class="hljs-built_in">console</span>.error(err);
    });
}
</code></pre>
<p>The <code>getSinglePost(&lt;you-slug&gt;)</code> function returns data about a single article, and you can render that data on the page.</p>
<pre><code class="lang-typescript"><span class="hljs-comment">// src/app/read/[slug]/page.tsx</span>

<span class="hljs-keyword">import</span> Newsletter <span class="hljs-keyword">from</span> <span class="hljs-string">"./Newsletter"</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> { getSinglePost, getPosts } <span class="hljs-keyword">from</span> <span class="hljs-string">"../../ghost-client"</span>
<span class="hljs-keyword">import</span> Image <span class="hljs-keyword">from</span> <span class="hljs-string">"next/image"</span>;
<span class="hljs-comment">// import icon</span>
<span class="hljs-keyword">import</span> { FaAngleLeft } <span class="hljs-keyword">from</span> <span class="hljs-string">"react-icons/fa"</span>;

<span class="hljs-comment">// types for typescript</span>
<span class="hljs-keyword">import</span> <span class="hljs-keyword">type</span> { Metadata } <span class="hljs-keyword">from</span> <span class="hljs-string">"next"</span>;
<span class="hljs-keyword">import</span> <span class="hljs-keyword">type</span> { PostOrPage } <span class="hljs-keyword">from</span> <span class="hljs-string">"@tryghost/content-api"</span>;

<span class="hljs-comment">// format the date</span>
<span class="hljs-keyword">import</span> { format } <span class="hljs-keyword">from</span> <span class="hljs-string">'date-fns'</span>

<span class="hljs-comment">// css for card</span>
<span class="hljs-keyword">import</span> <span class="hljs-string">"../../cards.min.css"</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">generateStaticParams</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">const</span> posts = <span class="hljs-keyword">await</span> getPosts()
  <span class="hljs-keyword">return</span> posts.map(<span class="hljs-function">(<span class="hljs-params">post</span>) =&gt;</span> ({
    slug: post.slug,
  }));
}


<span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">Read</span>(<span class="hljs-params">{ params }: { params: { slug: <span class="hljs-built_in">string</span> }; }</span>) </span>{

  <span class="hljs-keyword">const</span> getPost = <span class="hljs-keyword">await</span> getSinglePost(params.slug)

  <span class="hljs-keyword">return</span> (
    &lt;&gt;
      &lt;main className=<span class="hljs-string">"pt-8 pb-16 lg:pt-16 lg:pb-24 dark:bg-gray-900"</span>&gt;

        &lt;div className=<span class="hljs-string">"flex justify-between px-4 mx-auto max-w-screen-xl "</span>&gt;

          &lt;article className=<span class="hljs-string">"mx-auto w-full max-w-3xl prose prose-xl prose-p:text-gray-800  dark:prose-p:text-gray-100 sm:prose-base prose-a:no-underline prose-blue dark:prose-invert"</span>&gt;

            &lt;div className=<span class="hljs-string">"flex mb-4 w-full justify-between"</span>&gt;

              &lt;Link className=<span class="hljs-string">"inline-flex items-center"</span> href={<span class="hljs-string">`/`</span>}&gt;
                &lt;FaAngleLeft /&gt; Back
              &lt;/Link&gt;

              {
                getPost.primary_tag ? &lt;Link href={<span class="hljs-string">`/tags/<span class="hljs-subst">${getPost?.primary_tag.slug}</span>`</span>}&gt;
                  # {getPost?.primary_tag.name}
                &lt;/Link&gt; : <span class="hljs-string">""</span>
              }

            &lt;/div&gt;

            &lt;h1 className=<span class="hljs-string">"mb-4 text-3xl font-extrabold leading-tight text-gray-900 lg:mb-6 lg:text-4xl dark:text-white"</span>&gt;
              {getPost.title}
            &lt;/h1&gt;

            &lt;p className=<span class="hljs-string">"lead"</span>&gt;
              {getPost.excerpt}
            &lt;/p&gt;

            &lt;header className=<span class="hljs-string">"mb-4 lg:mb-6 not-format"</span>&gt;

              &lt;address className=<span class="hljs-string">"flex items-center mb-6 not-italic"</span>&gt;

                &lt;div className=<span class="hljs-string">"inline-flex items-center mr-3 text-sm text-gray-900 dark:text-white"</span>&gt;

                  &lt;Image width={<span class="hljs-number">32</span>} height={<span class="hljs-number">32</span>} className=<span class="hljs-string">"mr-4 w-10 h-10 rounded-full"</span> src={getPost?.primary_author.profile_image} alt={getPost?.primary_author.name} /&gt;
                  {
                    getPost.primary_author ? &lt;Link href={<span class="hljs-string">`/authors/<span class="hljs-subst">${getPost?.primary_author.slug}</span>`</span>} rel=<span class="hljs-string">"author"</span> className=<span class="hljs-string">"text-xl font-bold text-gray-800 dark:text-white"</span>&gt;{getPost?.primary_author.name}&lt;/Link&gt; : <span class="hljs-string">" "</span>
                  }

                  {
                    getPost.published_at ? &lt;time className=<span class="hljs-string">"text-base font-light text-gray-800 dark:text-white mx-1"</span> dateTime={getPost?.published_at} title={format(<span class="hljs-keyword">new</span> <span class="hljs-built_in">Date</span>(getPost?.published_at), <span class="hljs-string">'yyyy-MM-dd'</span>)}&gt;
                      {format(<span class="hljs-keyword">new</span> <span class="hljs-built_in">Date</span>(getPost?.published_at), <span class="hljs-string">'dd MMMM, yyyy'</span>)}
                    &lt;/time&gt; : <span class="hljs-string">""</span>
                  }

                  &lt;div className=<span class="hljs-string">"text-base w-1 h-1 rounded-full bg-black dark:bg-white mx-1"</span>&gt;&lt;/div&gt;

                  &lt;p className=<span class="hljs-string">"text-base font-light text-gray-500 dark:text-gray-400"</span>&gt; {getPost.reading_time}  Min Read&lt;/p&gt;

                &lt;/div&gt;

              &lt;/address&gt;

            &lt;/header&gt;

            &lt;figure&gt;
              &lt;Image className=<span class="hljs-string">"mx-auto"</span> width={<span class="hljs-number">1000</span>} height={<span class="hljs-number">250</span>} src={getPost.feature_image} alt={getPost.feature_image_alt} /&gt;
              &lt;figcaption className=<span class="hljs-string">"text-center"</span>
                dangerouslySetInnerHTML={{
                  __html: getPost?.feature_image_caption
                }}&gt;&lt;/figcaption&gt;
            &lt;/figure&gt;

            &lt;div dangerouslySetInnerHTML={{ __html: getPost?.html }}&gt;&lt;/div&gt;

          &lt;/article&gt;
        &lt;/div&gt;
      &lt;/main&gt;
      &lt;Newsletter /&gt;
    &lt;/&gt;
  )

}
<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> Read
</code></pre>
<p>You render the post's HTML data with <code>dangerouslySetInnerHTML</code> . But you need to write lots of CSS to handle the dynamic content coming from the Ghost CMS API. </p>
<p>To solve that, I used the <code>@tailwindcss/typography</code> package. I also downloaded <code>cards.min.css</code> from Ghost. Now you don't need to write a single line of CSS in your Next app.</p>
<p>Generate the static site with the <code>generateStaticParams</code> function. Before, we used to <code>getStaticProps</code> function.</p>
<pre><code class="lang-typescript"><span class="hljs-comment">// ghost-client.ts</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">generateStaticParams</span>(<span class="hljs-params"></span>) </span>{

  <span class="hljs-comment">// fetch All posts</span>

  <span class="hljs-keyword">const</span> posts = <span class="hljs-keyword">await</span> getPosts()

  <span class="hljs-comment">// return the slug </span>

  <span class="hljs-keyword">return</span> posts.map(<span class="hljs-function">(<span class="hljs-params">post</span>) =&gt;</span> ({
    slug: post.slug,
  }));

}
</code></pre>
<h3 id="heading-ia"> </h3>
<p>How to Build the Tag Page</p>
<p>I designed a simple tag page for the blog. The tag page shows articles related to the tags that are used. </p>
<p>You can also create a category page. Tag pages and category pages use the same logic and functionalities.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/04/ghostandnextjs-tag.png" alt="Tag page" width="600" height="400" loading="lazy">
<em>Tag page</em></p>
<p>Similar to the reading page, we'll get articles based on tags from the Ghost CMS API.</p>
<pre><code class="lang-typescript"><span class="hljs-comment">// ghost-client.ts</span>


<span class="hljs-comment">// return all posts realted to tag slug</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">getTagPosts</span>(<span class="hljs-params">tagSlug: <span class="hljs-built_in">string</span></span>) </span>{

  <span class="hljs-keyword">return</span> <span class="hljs-keyword">await</span> api.posts.browse({ filter: <span class="hljs-string">`tag:<span class="hljs-subst">${tagSlug}</span>`</span>, include: <span class="hljs-string">'count.posts'</span> })
    .catch(<span class="hljs-function"><span class="hljs-params">err</span> =&gt;</span> {
      <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> <span class="hljs-built_in">Error</span>(err)
    });
  ;

}

<span class="hljs-comment">// return all the slugs to build static with generateStaticParams</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">getAllTags</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">return</span> <span class="hljs-keyword">await</span> api.tags.browse({
    limit: <span class="hljs-string">"all"</span>
  }).catch(<span class="hljs-function"><span class="hljs-params">err</span> =&gt;</span> {
    <span class="hljs-built_in">console</span>.log(err)
  })
}
</code></pre>
<p>The <code>getTagPosts(&lt;tag-slug&gt;)</code> function returns all the available posts related to a specific tag. </p>
<p>After receiving all posts with <code>getTagPosts()</code>, we render all posts with the help of the <code>map()</code> method.</p>
<pre><code class="lang-typescript"><span class="hljs-comment">// src/app/tag/[slug]/page.tsx</span>

<span class="hljs-keyword">import</span> React <span class="hljs-keyword">from</span> <span class="hljs-string">'react'</span>
<span class="hljs-keyword">import</span> Card <span class="hljs-keyword">from</span> <span class="hljs-string">"../../Card"</span>

<span class="hljs-keyword">import</span> { getTagPosts, getAllTags } <span class="hljs-keyword">from</span> <span class="hljs-string">"../../ghost-client"</span>

<span class="hljs-keyword">import</span> { notFound } <span class="hljs-keyword">from</span> <span class="hljs-string">'next/navigation'</span>;

<span class="hljs-keyword">import</span> <span class="hljs-keyword">type</span> { PostsOrPages } <span class="hljs-keyword">from</span> <span class="hljs-string">"@tryghost/content-api"</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">generateStaticParams</span>(<span class="hljs-params"></span>) </span>{

  <span class="hljs-keyword">const</span> allTags: Tags = <span class="hljs-keyword">await</span> getAllTags()

  <span class="hljs-keyword">let</span> allTagsItem: { slug: <span class="hljs-built_in">string</span> }[] = []

<span class="hljs-comment">// genrate the slug for static site</span>

  allTags?.map(<span class="hljs-function"><span class="hljs-params">item</span> =&gt;</span> {
    allTagsItem.push({
      slug: item.slug,
    })
  })

  <span class="hljs-keyword">return</span> allTagsItem

}


<span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">Tag</span>(<span class="hljs-params">{ params }: { params: { slug: <span class="hljs-built_in">string</span> }; }</span>) </span>{

  <span class="hljs-keyword">let</span> tagPosts: PostsOrPages = <span class="hljs-keyword">await</span> getTagPosts(params.slug)

<span class="hljs-comment">// Handling 404 error</span>

  <span class="hljs-keyword">if</span> (tagPosts.length === <span class="hljs-number">0</span>) {
    notFound()
  }

  <span class="hljs-keyword">return</span> (
    &lt;aside aria-label=<span class="hljs-string">"Related articles"</span> className=<span class="hljs-string">"py-8 lg:py-24 dark:bg-gray-800"</span>&gt;

      &lt;div className=<span class="hljs-string">"px-4 mx-auto max-w-screen-xl"</span>&gt;

        &lt;h2 className=<span class="hljs-string">"mb-8 text-2xl font-bold text-gray-900 dark:text-white"</span>&gt;
          More articles <span class="hljs-keyword">from</span> {params.slug.split(<span class="hljs-string">"-"</span>).join(<span class="hljs-string">" "</span>)}
        &lt;/h2&gt;

        &lt;div className=<span class="hljs-string">"container my-12 mx-auto grid grid-cols-1 gap-12 md:gap-12 lg:gap-12  lg:grid-cols-3  md:grid-cols-2 xl:grid-cols-4 2xl:grid-cols-4 "</span>&gt;

          {
            tagPosts.map(
              <span class="hljs-function"><span class="hljs-params">item</span> =&gt;</span> &lt;Card key={item.uuid} item={item} /&gt;
            )
          }

        &lt;/div&gt;

      &lt;/div&gt;

    &lt;/aside&gt;
  )

}

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> Tag
</code></pre>
<p>Generate the static site with the <code>generateStaticParams</code> function. It helps to generate slugs of the static build.</p>
<pre><code class="lang-typescript"><span class="hljs-comment">// ghost-client.ts</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">getAllTags</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">return</span> <span class="hljs-keyword">await</span> api.tags.browse({
    limit: <span class="hljs-string">"all"</span>
  }).catch(<span class="hljs-function"><span class="hljs-params">err</span> =&gt;</span> {
    <span class="hljs-built_in">console</span>.log(err)
  })
}
</code></pre>
<h3 id="heading-how-to-build-the-author-page">How to Build the Author Page</h3>
<p>The last and one of the most important pages for the blog site is the author page. This is where readers can learn more about the author. </p>
<p>For the demo blog, I designed a basic page for the author. </p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/04/nextandghostauthor.png" alt="Image" width="600" height="400" loading="lazy">
<em>Author page</em></p>
<p>We'll build this in a similar way as we built the tag page. First, we get the author's metadata and author posts from the Ghost CMS API. </p>
<pre><code class="lang-typescript"><span class="hljs-comment">// ghost-client.ts</span>


<span class="hljs-comment">// get author meta Data</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">getSingleAuthor</span>(<span class="hljs-params">authorSlug: <span class="hljs-built_in">string</span></span>) </span>{
  <span class="hljs-keyword">return</span> <span class="hljs-keyword">await</span> api.authors
    .read({
      slug: authorSlug
    }, { include: [<span class="hljs-string">"count.posts"</span>] })
    .catch(<span class="hljs-function"><span class="hljs-params">err</span> =&gt;</span> {
      <span class="hljs-built_in">console</span>.log(err)
    });

}

<span class="hljs-comment">// get author related posts</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">getSingleAuthorPosts</span>(<span class="hljs-params">authorSlug: <span class="hljs-built_in">string</span></span>) </span>{
  <span class="hljs-keyword">return</span> <span class="hljs-keyword">await</span> api.posts.browse({ filter: <span class="hljs-string">`authors:<span class="hljs-subst">${authorSlug}</span>`</span> })
    .catch(<span class="hljs-function"><span class="hljs-params">err</span> =&gt;</span> {
      <span class="hljs-built_in">console</span>.log(err)
    })
};

<span class="hljs-comment">// get All author from Ghost CMS for generateStaticParams</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">getAllAuthors</span>(<span class="hljs-params"></span>) </span>{

  <span class="hljs-keyword">return</span> <span class="hljs-keyword">await</span> api.authors
    .browse({
      limit: <span class="hljs-string">"all"</span>
    })
    .catch(<span class="hljs-function"><span class="hljs-params">err</span> =&gt;</span> {
      <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> <span class="hljs-built_in">Error</span>(err)
    });

}
</code></pre>
<p>The <code>getSingleAuthor(&lt;author-slug&gt;)</code> returns data about a single author based on the author slug, and the  <code>getSingleAuthorPosts(&lt;author-slug&gt;)</code> function returns all posts related to the author.</p>
<p>We render the posts data with the help of the <code>map()</code> method. </p>
<pre><code class="lang-typescript"><span class="hljs-comment">// src/app/author/[slug]/page.tsx</span>

<span class="hljs-keyword">import</span> React <span class="hljs-keyword">from</span> <span class="hljs-string">'react'</span>;
<span class="hljs-keyword">import</span> Link <span class="hljs-keyword">from</span> <span class="hljs-string">"next/link"</span>;
<span class="hljs-keyword">import</span> { FaFacebook, FaTwitter, FaGlobe } <span class="hljs-keyword">from</span> <span class="hljs-string">"react-icons/fa"</span>;
<span class="hljs-keyword">import</span> Card <span class="hljs-keyword">from</span> <span class="hljs-string">"../../Card"</span>

<span class="hljs-keyword">import</span> { getSingleAuthor, getSingleAuthorPost, getAllAuthors } <span class="hljs-keyword">from</span> <span class="hljs-string">"../../ghost-client"</span>

<span class="hljs-keyword">import</span> Image <span class="hljs-keyword">from</span> <span class="hljs-string">'next/image'</span>;
<span class="hljs-keyword">import</span> { notFound } <span class="hljs-keyword">from</span> <span class="hljs-string">'next/navigation'</span>;

<span class="hljs-keyword">import</span> <span class="hljs-keyword">type</span> { Author, PostsOrPages } <span class="hljs-keyword">from</span> <span class="hljs-string">"@tryghost/content-api"</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">generateStaticParams</span>(<span class="hljs-params"></span>) </span>{

  <span class="hljs-keyword">const</span> allAuthor: Author[] = <span class="hljs-keyword">await</span> getAllAuthors()


  <span class="hljs-keyword">let</span> allAuthorItem: { slug: <span class="hljs-built_in">string</span> }[] = []

  allAuthor.map(<span class="hljs-function"><span class="hljs-params">item</span> =&gt;</span> {
    allAuthorItem.push({
      slug: item.slug,
    })
  })
  <span class="hljs-keyword">return</span> allAuthorItem

}


<span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">AuthorPage</span>(<span class="hljs-params">{ params }: { params: { slug: <span class="hljs-built_in">string</span> }; }</span>) </span>{

  <span class="hljs-keyword">const</span> getAuthor: Author = <span class="hljs-keyword">await</span> getSingleAuthor(params.slug)

  <span class="hljs-keyword">const</span> allAuthor: PostsOrPages = <span class="hljs-keyword">await</span> getSingleAuthorPost(params.slug)

<span class="hljs-comment">// Handling 404 errors</span>
  <span class="hljs-keyword">if</span> (allAuthor?.length === <span class="hljs-number">0</span>) {
    notFound()
  }

  <span class="hljs-keyword">return</span> (
    &lt;&gt;
      &lt;section className=<span class="hljs-string">"dark:bg-gray-900"</span>&gt;
        &lt;div className=<span class="hljs-string">"py-8 px-4 mx-auto max-w-screen-xl lg:py-16 lg:px-6"</span>&gt;

          &lt;div className=<span class="hljs-string">" p-10 text-gray-500 sm:text-lg dark:text-gray-400"</span>&gt;

            {
              getAuthor?.profile_image !== <span class="hljs-literal">undefined</span> ? &lt;Image height={<span class="hljs-number">30</span>} width={<span class="hljs-number">30</span>} className=<span class="hljs-string">"w-36 h-36 p-2 rounded-full mx-auto ring-2 ring-gray-300 dark:ring-gray-500"</span> src={getAuthor?.profile_image} alt={getAuthor?.name} /&gt; : <span class="hljs-string">""</span>
            }

            {
              getAuthor?.name ? &lt;h2 className=<span class="hljs-string">"mb-4 mt-4 text-4xl tracking-tight font-bold text-center text-gray-900 dark:text-white"</span>&gt;
                {getAuthor?.name.split(<span class="hljs-string">" "</span>)[<span class="hljs-number">0</span>]}
                &lt;span className=<span class="hljs-string">"font-extrabold"</span>&gt;
                  {getAuthor?.name?.split(<span class="hljs-string">" "</span>)[<span class="hljs-number">1</span>]}
                &lt;/span&gt;
              &lt;/h2&gt; : <span class="hljs-string">""</span>
            }

            &lt;p className=<span class="hljs-string">"mb-4 font-light text-center"</span>&gt;{getAuthor?.bio} &lt;/p&gt;


            &lt;ul className=<span class="hljs-string">"flex flex-wrap p-4 justify-center md:space-x-8 md:mt-0 md:text-sm md:font-medium"</span>&gt;

              {
                (getAuthor?.website !== <span class="hljs-literal">null</span>) ? (&lt;li&gt;
                  &lt;Link href={getAuthor?.website} className=<span class="hljs-string">"block py-2 pl-3 pr-4 text-gray-700 hover:text-blue-700 dark:hover:text-blue-700 rounded md:p-0 dark:text-white"</span> aria-current=<span class="hljs-string">"page"</span>&gt;
                    &lt;FaGlobe /&gt;
                  &lt;<span class="hljs-regexp">/Link&gt; &lt;/</span>li&gt;) : <span class="hljs-string">" "</span>


              }

              {
                (getAuthor?.twitter !== <span class="hljs-literal">null</span>) ? (&lt;li&gt;
                  &lt;Link href={getAuthor?.twitter} className=<span class="hljs-string">"block py-2 pl-3 pr-4 text-gray-700 rounded hover:text-blue-700 dark:hover:text-blue-700 md:p-0 dark:text-white"</span> aria-current=<span class="hljs-string">"page"</span>&gt;
                    &lt;FaTwitter /&gt;
                  &lt;/Link&gt;
                &lt;/li&gt;) : <span class="hljs-string">" "</span>
              }

              {
                (getAuthor?.facebook !== <span class="hljs-literal">null</span> &amp;&amp; getAuthor.facebook !== <span class="hljs-literal">undefined</span>) ? (&lt;li&gt;
                  &lt;Link href={getAuthor?.facebook}
                    className=<span class="hljs-string">"block py-2 pl-3 pr-4 text-gray-700 rounded  hover:text-blue-700 dark:hover:text-blue-700 md:p-0 dark:text-white"</span>&gt; &lt;FaFacebook /&gt;
                  &lt;/Link&gt;
                &lt;/li&gt;) : <span class="hljs-string">" "</span>

              }

            &lt;/ul&gt;

          &lt;/div&gt;
        &lt;/div&gt;
      &lt;/section&gt;

      &lt;aside aria-label=<span class="hljs-string">"Related articles"</span> className=<span class="hljs-string">"py-8 lg:py-24 dark:bg-gray-800"</span>&gt;
        &lt;div className=<span class="hljs-string">"px-4 mx-auto max-w-screen-xl"</span>&gt;

          &lt;h2 className=<span class="hljs-string">"mb-8 text-2xl font-bold text-gray-900 dark:text-white"</span>&gt;
            More articles <span class="hljs-keyword">from</span> {getAuthor?.name}
          &lt;/h2&gt;

          &lt;div className=<span class="hljs-string">"container my-12 mx-auto grid grid-cols-1 gap-12 md:gap-12 lg:gap-12  lg:grid-cols-3  md:grid-cols-2 xl:grid-cols-4 2xl:grid-cols-4 "</span>&gt;

            {
              allAuthor?.map(<span class="hljs-function"><span class="hljs-params">item</span> =&gt;</span> &lt;Card key={item?.uuid} item={item} /&gt;)
            }

          &lt;/div&gt;
        &lt;/div&gt;
      &lt;/aside&gt;


    &lt;/&gt;
  )

}
<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> AuthorPage
</code></pre>
<p>To generate the author slug for the static site, we need to use the <code>generateStaticParams</code> function. We do not need anything else to build the static site.</p>
<pre><code class="lang-typescript"><span class="hljs-comment">// ghost-client.ts</span>


<span class="hljs-comment">// Build Static Site </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">generateStaticParams</span>(<span class="hljs-params"></span>) </span>{

  <span class="hljs-keyword">const</span> allAuthor: Author[] = <span class="hljs-keyword">await</span> getAllAuthors()


  <span class="hljs-keyword">let</span> allAuthorItem: { slug: <span class="hljs-built_in">string</span> }[] = []

  allAuthor.map(<span class="hljs-function"><span class="hljs-params">item</span> =&gt;</span> {
    allAuthorItem.push({
      slug: item.slug,
    })
  })
  <span class="hljs-keyword">return</span> allAuthorItem

}
</code></pre>
<h3 id="heading-how-to-build-single-pages">How to Build Single Pages</h3>
<p>For single pages like About, Contact, Privacy Policy, and so on, you can also create them with the Ghost Content API.</p>
<p>Our single-page design looks like this:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/04/single-blog.png" alt="single blog page" width="600" height="400" loading="lazy">
<em>single blog page</em></p>
<p>Firstly, you need to fetch all pages and the single pages data from the Ghost Content API. </p>
<pre><code class="lang-typescript"><span class="hljs-comment">// ghost-client.tsx</span>

<span class="hljs-comment">// fetch all pages</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">getSinglePage</span>(<span class="hljs-params">pageSlug: <span class="hljs-built_in">string</span></span>) </span>{
  <span class="hljs-keyword">return</span> <span class="hljs-keyword">await</span> api.pages
    .read({
      slug: pageSlug
    })
    .catch(<span class="hljs-function"><span class="hljs-params">err</span> =&gt;</span> {
      <span class="hljs-built_in">console</span>.error(err);
    });
}

<span class="hljs-comment">// single page data </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">getSinglePage</span>(<span class="hljs-params">pageSlug: <span class="hljs-built_in">string</span></span>) </span>{
  <span class="hljs-keyword">return</span> <span class="hljs-keyword">await</span> api.pages
    .read({
      slug: pageSlug
    }, { include: [<span class="hljs-string">"tags"</span>] })
    .catch(<span class="hljs-function"><span class="hljs-params">err</span> =&gt;</span> {
      <span class="hljs-built_in">console</span>.error(err);
    });
}
</code></pre>
<p>The <code>getSinglePage(page-slug)</code> function returns the single page data based on the page slug, and the <code>getAllPages()</code> function returns all the available published page data to generate the dynamic params with the <code>generateStaticParams()</code> function. </p>
<pre><code class="lang-typescript"><span class="hljs-comment">// src/app/pages/[slug]/page.tsx</span>

<span class="hljs-keyword">import</span> { getSinglePage, getAllPages } <span class="hljs-keyword">from</span> <span class="hljs-string">"../../ghost-client"</span>
<span class="hljs-keyword">import</span> { notFound } <span class="hljs-keyword">from</span> <span class="hljs-string">'next/navigation'</span>;
<span class="hljs-keyword">import</span> <span class="hljs-keyword">type</span> { PostOrPage } <span class="hljs-keyword">from</span> <span class="hljs-string">"@tryghost/content-api"</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">"../../cards.min.css"</span>

<span class="hljs-comment">// genrate Static slug or params for blog</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">generateStaticParams</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">const</span> pages = <span class="hljs-keyword">await</span> getAllPages()

  <span class="hljs-keyword">return</span> pages.map(<span class="hljs-function">(<span class="hljs-params">post</span>) =&gt;</span> ({
    slug: post.slug,
  }));
}

<span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">Pages</span>(<span class="hljs-params">{ params }: { params: { slug: <span class="hljs-built_in">string</span> }; }</span>) </span>{

<span class="hljs-comment">// fetch single page</span>
  <span class="hljs-keyword">const</span> getPage = <span class="hljs-keyword">await</span> getSinglePage(params.slug)

  <span class="hljs-comment">// handle 404 error</span>
  <span class="hljs-keyword">if</span> (!getPage) {
    notFound()
  }

  <span class="hljs-keyword">return</span> (
    &lt;&gt;
      &lt;main className=<span class="hljs-string">"pt-8 pb-16 lg:pt-16 lg:pb-24 dark:bg-gray-900"</span>&gt;
        &lt;div className=<span class="hljs-string">"flex justify-between px-4 mx-auto max-w-screen-xl "</span>&gt;

          &lt;article className=<span class="hljs-string">"mx-auto w-full max-w-3xl prose prose-xl prose-p:text-gray-800  dark:prose-p:text-gray-100 sm:prose-base prose-a:no-underline prose-blue dark:prose-invert"</span>&gt;


            &lt;h1 className=<span class="hljs-string">"mb-14 text-3xl font-extrabold leading-tight text-gray-900 lg:mb-6 lg:text-4xl dark:text-white"</span>&gt;
              {getPage.title}
            &lt;/h1&gt;

            &lt;div dangerouslySetInnerHTML={{ __html: getPage?.html }}&gt;&lt;/div&gt;

          &lt;/article&gt;
        &lt;/div&gt;
      &lt;/main&gt;
    &lt;/&gt;
  )

}
<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> Pages
</code></pre>
<h3 id="heading-how-to-handle-pagination">How to Handle Pagination</h3>
<p>Pagination helps speed up your site as well as divide your site into smaller parts, more digestible pages. You can link your posts with each other with <code>prev</code> and <code>next</code>. </p>
<pre><code class="lang-json">meta:{
    pagination: { page: <span class="hljs-number">1</span>, limit: <span class="hljs-number">10</span>, pages: <span class="hljs-number">2</span>, total: <span class="hljs-number">12</span>, next: <span class="hljs-number">2</span>, prev: <span class="hljs-literal">null</span> }
 }
</code></pre>
<p>Firstly, we'll create a <code>Pagination.tsx</code> file as a React component.</p>
<pre><code class="lang-typescript"><span class="hljs-comment">// Pagination.tsx</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> { Pagination } <span class="hljs-keyword">from</span> <span class="hljs-string">"@tryghost/content-api"</span>

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">PaginationItem</span>(<span class="hljs-params">{ item }: { item: Pagination }</span>) </span>{

  <span class="hljs-keyword">let</span> paginationItems = []

  <span class="hljs-keyword">for</span> (<span class="hljs-keyword">let</span> index = <span class="hljs-number">1</span>; index &lt;= item?.pages; index++) {
    paginationItems.push(&lt;li key={index * <span class="hljs-number">2</span>} &gt;&lt;Link href={index === <span class="hljs-number">1</span> ? <span class="hljs-string">"/"</span> : <span class="hljs-string">`/pagination/<span class="hljs-subst">${index}</span>`</span>} className=<span class="hljs-string">"px-3 py-2 leading-tight bg-blue-100 hover:bg-blue-200 border-transparent border rounded-lg text-black dark:bg-gray-800 dark:text-gray-400 mx-2 dark:hover:bg-gray-700 dark:hover:text-white"</span>&gt;
      {index}
    &lt;<span class="hljs-regexp">/Link&gt;&lt;/</span>li&gt;)
  }

  <span class="hljs-keyword">return</span> (

    &lt;nav aria-label=<span class="hljs-string">"pagination"</span> className=<span class="hljs-string">"mx-auto my-20 container"</span>&gt;

      &lt;ul className=<span class="hljs-string">"mx-auto flex justify-center -space-x-px"</span>&gt;

        &lt;li&gt;
          {
            item.prev ? &lt;Link href={item.prev === <span class="hljs-number">1</span> ? <span class="hljs-string">"/"</span> : <span class="hljs-string">`/pagination/<span class="hljs-subst">${item.prev}</span>`</span>} className=<span class="hljs-string">"px-3 py-2 mr-2 border border-transparent rounded-md  leading-tight bg-white hover:text-blue-700 dark:bg-gray-800 dark:text-gray-400
              dark:hover:bg-gray-700 dark:hover:text-white"</span>&gt;
              Prev
            &lt;/Link&gt; : <span class="hljs-string">" "</span>
          }
        &lt;/li&gt;

        {paginationItems}

        &lt;li&gt;
          {
            item.next ? &lt;Link href={<span class="hljs-string">`/pagination/<span class="hljs-subst">${item.next}</span>`</span>} className=<span class="hljs-string">"px-3 py-2 ml-2 border border-transparent rounded-md leading-tight bg-white hover:text-blue-700 dark:bg-gray-800 dark:text-gray-400
            dark:hover:bg-gray-700 dark:hover:text-white"</span>&gt;
              Next
            &lt;/Link&gt; : <span class="hljs-string">" "</span>
          }
        &lt;/li&gt;


      &lt;/ul&gt;

    &lt;/nav&gt;

  )

}


<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> PaginationItem
</code></pre>
<p>When you call the <code>api.posts.browse({ limit: 10 })</code> request, the API endpoint returns ten posts and a <code>meta</code> object with <code>pagination</code>.</p>
<h4 id="heading-the-returned-apipostsbrowse-limit-10-data-look-like-this">The returned <code>api.posts.browse({ limit: 10 })</code> data look like this:</h4>
<pre><code class="lang-json"> [
  {title: 'Demo Posts with Nextjs and Ghost Editor',... },
  {title: Trigger the hook and rebuild the nextjs site',... }

meta:{
    pagination: { page: <span class="hljs-number">1</span>, limit: <span class="hljs-number">10</span>, pages: <span class="hljs-number">2</span>, total: <span class="hljs-number">12</span>, next: <span class="hljs-number">2</span>, prev: <span class="hljs-literal">null</span> }
  }
]
</code></pre>
<p>Now based on <code>meta</code>, we can create pagination and pass <code>meta.pagination</code> as props to the <code>Pagination</code> component.</p>
<pre><code class="lang-typescript"><span class="hljs-comment">// src/app/page.tsx</span>

<span class="hljs-keyword">import</span> { getPosts } <span class="hljs-keyword">from</span> <span class="hljs-string">"./ghost-client"</span>
<span class="hljs-keyword">import</span> Pagination <span class="hljs-keyword">from</span> <span class="hljs-string">"./Pagination"</span>

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> <span class="hljs-keyword">async</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> getPost = <span class="hljs-keyword">await</span> getPosts()

  <span class="hljs-keyword">const</span> AllPostForSerach = <span class="hljs-keyword">await</span> getSearchPosts()

  <span class="hljs-keyword">return</span> (
    &lt;&gt;
     {<span class="hljs-comment">/* rest of code  */</span>}
      &lt;Pagination item={getPost.meta.pagination} /&gt;
    &lt;/&gt;
  )
}
</code></pre>
<p>To enable dynamic pagination, we'll create a <code>src/app/pagination/[item]/page.tsx</code> route in the blog. You can use whatever name you want for the pagination route.</p>
<pre><code class="lang-typescript"><span class="hljs-comment">// ghost-client.tsx</span>

<span class="hljs-comment">// return all posts for generateStaticParams</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">getPosts</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">return</span> <span class="hljs-keyword">await</span> api.posts
    .browse({
      include: [<span class="hljs-string">"tags"</span>, <span class="hljs-string">"authors"</span>],
      limit: <span class="hljs-number">10</span>
    })
    .catch(<span class="hljs-function"><span class="hljs-params">err</span> =&gt;</span> {
      <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> <span class="hljs-built_in">Error</span>(err)
    });
}

<span class="hljs-comment">// </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">getPaginationPosts</span>(<span class="hljs-params">page: <span class="hljs-built_in">number</span></span>) </span>{
  <span class="hljs-keyword">return</span> <span class="hljs-keyword">await</span> api.posts
    .browse({
      include: [<span class="hljs-string">"tags"</span>, <span class="hljs-string">"authors"</span>],
      limit: <span class="hljs-number">10</span>,
      page: page
    })
    .catch(<span class="hljs-function"><span class="hljs-params">err</span> =&gt;</span> {
      <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> <span class="hljs-built_in">Error</span>(err)
    });
}
</code></pre>
<p>The <code>getPosts</code> is used to render the <code>Pagination</code> component on the pagination page. The important part is the <code>getPaginationPosts(&lt;pagination-page-number&gt;)</code> function, which returns posts based on the pagination page number.</p>
<pre><code class="lang-typescript"><span class="hljs-comment">// src/app/pagination/[item]/page.tsx</span>

<span class="hljs-keyword">import</span> { getPaginationPosts, getPosts } <span class="hljs-keyword">from</span> <span class="hljs-string">"../../ghost-client"</span>
<span class="hljs-keyword">import</span> Card <span class="hljs-keyword">from</span> <span class="hljs-string">'../../Card'</span>
<span class="hljs-keyword">import</span> PaginationItem <span class="hljs-keyword">from</span> <span class="hljs-string">"../../Pagination"</span>
<span class="hljs-keyword">import</span> <span class="hljs-keyword">type</span> { Metadata } <span class="hljs-keyword">from</span> <span class="hljs-string">"next"</span>;
<span class="hljs-keyword">import</span> <span class="hljs-keyword">type</span> { PostsOrPages } <span class="hljs-keyword">from</span> <span class="hljs-string">"@tryghost/content-api"</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">generateStaticParams</span>(<span class="hljs-params"></span>) </span>{

  <span class="hljs-keyword">const</span> posts:PostsOrPages = <span class="hljs-keyword">await</span> getPosts()

  <span class="hljs-keyword">let</span> paginationItem: { item: <span class="hljs-built_in">number</span> }[] = []

  <span class="hljs-keyword">for</span> (<span class="hljs-keyword">let</span> index = <span class="hljs-number">1</span>; index &lt;= posts?.meta.pagination.pages; index++) {
    paginationItem.push({
      item: index,
    })

  }

  <span class="hljs-keyword">return</span> paginationItem

}



<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> <span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">Pagination</span>(<span class="hljs-params">{ params }: { params: { item: <span class="hljs-built_in">string</span> }; }</span>) </span>{

  <span class="hljs-keyword">let</span> getParams: <span class="hljs-built_in">number</span> = <span class="hljs-built_in">Number</span>.parseInt(params.item)

  <span class="hljs-keyword">const</span> getPost: PostsOrPages = <span class="hljs-keyword">await</span> getPaginationPosts(getParams)

  <span class="hljs-keyword">return</span> (
    &lt;&gt;

      &lt;main className=<span class="hljs-string">"container my-12 mx-auto grid grid-cols-1 gap-2 md:gap-3 lg:gap-4 lg:grid-cols-3 md:grid-cols-2 xl:grid-cols-4 2xl:grid-cols-4"</span>&gt;

        {
          getPost?.map(
            <span class="hljs-function"><span class="hljs-params">item</span> =&gt;</span> {
              <span class="hljs-keyword">return</span> &lt;Card key={item.uuid} item={item} /&gt;
            })
        }
      &lt;/main&gt;

      &lt;PaginationItem item={getPost.meta.pagination} /&gt;

    &lt;/&gt;
  )
}
</code></pre>
<h3 id="heading-nextjs-seo">Next.js SEO</h3>
<p>If you are a blogger, you know how important SEO is in helping people find your blog and your articles. For SEO, Next.js provides a <code>generateMetadata</code> function to generate dynamic SEO metadata for your site. This means that you don't need any additional packages for SEO.  </p>
<p>For the purpose of this example, I'll explain how to enable SEO for the blog only on the Homepage and the Reading page. You can use the same logic to enable it on any of your other pages. </p>
<p>First, let's see how to enable SEO on the Homepage:</p>
<pre><code class="lang-typescript"><span class="hljs-comment">// ghost-client.ts</span>


<span class="hljs-comment">// Get you settings meta data from Ghost CMS</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">getNavigation</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">return</span> <span class="hljs-keyword">await</span> api.settings.browse()
}
</code></pre>
<pre><code class="lang-typescript"><span class="hljs-comment">// src/app/page.tsx</span>

<span class="hljs-keyword">import</span> { getNavigation } <span class="hljs-keyword">from</span> <span class="hljs-string">"./ghost-client"</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">generateMetadata</span>(<span class="hljs-params"></span>): <span class="hljs-title">Promise</span>&lt;<span class="hljs-title">Metadata</span>&gt; </span>{
  <span class="hljs-keyword">const</span> Metadata = <span class="hljs-keyword">await</span> getNavigation()
  <span class="hljs-keyword">return</span> {
    title: Metadata.title,
    description: Metadata.description,
    keywords: [<span class="hljs-string">'Next.js'</span>, <span class="hljs-string">'React'</span>, <span class="hljs-string">'JavaScript'</span>],
  }
}
</code></pre>
<p>Now we'll see how to enable SEO on the Reading page:</p>
<pre><code class="lang-typescript"><span class="hljs-comment">// ghost-client.ts</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">getSinglePost</span>(<span class="hljs-params">postSlug: <span class="hljs-built_in">string</span></span>) </span>{
  <span class="hljs-keyword">return</span> <span class="hljs-keyword">await</span> api.posts
    .read({
      slug: postSlug
    }, { include: [<span class="hljs-string">"tags"</span>, <span class="hljs-string">"authors"</span>] })
    .catch(<span class="hljs-function"><span class="hljs-params">err</span> =&gt;</span> {
      <span class="hljs-built_in">console</span>.error(err);
    });
}
</code></pre>
<p>The <code>generateMetadata</code> have params props, which help access the slug. Then, based on the slug, we get the data and return it. </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">generateMetadata</span>(<span class="hljs-params">{ params }: { params: { slug: <span class="hljs-built_in">string</span> }; }</span>): <span class="hljs-title">Promise</span>&lt;<span class="hljs-title">Metadata</span>&gt; </span>{

  <span class="hljs-keyword">const</span> metaData: PostOrPage = <span class="hljs-keyword">await</span> getSinglePost(params.slug)

  <span class="hljs-keyword">let</span> tags = metaData?.tags.map(<span class="hljs-function"><span class="hljs-params">item</span> =&gt;</span> item.name)

  <span class="hljs-keyword">return</span> {
    title: metaData.title,
    description: metaData.description,
    keywords: tags,
    openGraph: {
      title: metaData.title,
      description: metaData.excpet,
      url: metaData.url,
      keywords: tags,
      images: [
        {
          url: metaData.feature_image,
        },
      ],
      locale: metaData.locale,
      <span class="hljs-keyword">type</span>: <span class="hljs-string">'website'</span>,
    },
  }
}
</code></pre>
<h3 id="heading-how-to-enable-search">How to Enable Search</h3>
<p>Enabling search on a static blog is hard to do from scratch. Instead, you can use a third-party Node page like <a target="_blank" href="https://github.com/oramasearch/orama">Orama</a> or <a target="_blank" href="https://github.com/nextapps-de/flexsearch">Flex search</a>.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/04/searchbarinnextjs.gif" alt="Image" width="600" height="400" loading="lazy"></p>
<p>For our demo, we created a very simple search bar functionality without installing any additional packages. </p>
<p>Firstly, we get all posts from the Ghost CMS API.</p>
<pre><code class="lang-typescript"><span class="hljs-comment">// ghost-client.ts</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">getSearchPosts</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">return</span> <span class="hljs-keyword">await</span> api.posts.browse({ limit: <span class="hljs-string">"all"</span>}).catch(<span class="hljs-function"><span class="hljs-params">err</span> =&gt;</span> {
    <span class="hljs-built_in">console</span>.log(err)
  });
</code></pre>
<p>After we convert it into a string with the help of <code>JSON.stringify()</code>, we then create a new <code>search.json</code> file. On every request, it updates or rewrites our <code>search.json</code> file.</p>
<pre><code class="lang-typescript"><span class="hljs-comment">// src/app/page.tsx</span>

<span class="hljs-keyword">import</span> {  getSearchPosts } <span class="hljs-keyword">from</span> <span class="hljs-string">"./ghost-client"</span>
<span class="hljs-keyword">import</span> * <span class="hljs-keyword">as</span> fs <span class="hljs-keyword">from</span> <span class="hljs-string">'node:fs'</span>;



<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> <span class="hljs-keyword">async</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-comment">// get All posts for search </span>
  <span class="hljs-keyword">const</span> AllPostForSerach = <span class="hljs-keyword">await</span> getSearchPosts()

  <span class="hljs-comment">// Enable getSearch  </span>

  <span class="hljs-keyword">try</span> {

    <span class="hljs-keyword">const</span> jsonString = <span class="hljs-built_in">JSON</span>.stringify(AllPostForSerach)

    fs.writeFile(<span class="hljs-string">'search.json'</span>, jsonString, <span class="hljs-string">'utf8'</span>, <span class="hljs-function"><span class="hljs-params">err</span> =&gt;</span> {
      <span class="hljs-keyword">if</span> (err) {
        <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'Error writing file'</span>, err)
      } <span class="hljs-keyword">else</span> {
        <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'Successfully wrote file'</span>)
      }
    })

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


  <span class="hljs-keyword">return</span> (
    &lt;&gt;
      &lt;main className=<span class="hljs-string">"container my-12 mx-auto grid grid-cols-1 gap-2 md:gap-3 lg:gap-4 lg:grid-cols-3 md:grid-cols-2 xl:grid-cols-4 2xl:grid-cols-4"</span>&gt;
      {<span class="hljs-comment">/* rest code... */</span>}
      &lt;/main&gt;
    &lt;/&gt;
  )
}
</code></pre>
<p>When you enter the text in the search input, based on the text query, we compare the query or text in the <code>serach.json</code> file data. If it matches the article title with the query, then we store the <code>searchPost</code> variable, and finally we render the stored data in the <code>searchPost</code> variable page.</p>
<pre><code class="lang-typescript"><span class="hljs-string">"use client"</span>

<span class="hljs-keyword">import</span> React, { useEffect, useState } <span class="hljs-keyword">from</span> <span class="hljs-string">'react'</span>;
<span class="hljs-keyword">import</span> * <span class="hljs-keyword">as</span> Popover <span class="hljs-keyword">from</span> <span class="hljs-string">'@radix-ui/react-popover'</span>;
<span class="hljs-keyword">import</span> { FaSearch } <span class="hljs-keyword">from</span> <span class="hljs-string">"react-icons/fa"</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> searchData <span class="hljs-keyword">from</span> <span class="hljs-string">'../../search.json'</span>
<span class="hljs-keyword">import</span> <span class="hljs-keyword">type</span> { PostOrPage } <span class="hljs-keyword">from</span> <span class="hljs-string">"@tryghost/content-api"</span>


<span class="hljs-keyword">let</span> searchPost: PostOrPage[] = []


<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">Search</span>(<span class="hljs-params"></span>) </span>{

  <span class="hljs-keyword">const</span> [query, setQuery] = useState(<span class="hljs-literal">null</span>)

  useEffect(<span class="hljs-function">() =&gt;</span> {

    searchPost.length = <span class="hljs-number">0</span>;

    searchData.map(<span class="hljs-function">(<span class="hljs-params">item: PostOrPage</span>) =&gt;</span> {

      <span class="hljs-keyword">if</span> (item?.title.trim().toLowerCase().includes(query?.trim().toLowerCase())) {
        searchPost.push(item)
      }

    })

  }, [query])


  <span class="hljs-keyword">return</span> (
    &lt;Popover.Root&gt;
      &lt;Popover.Trigger asChild&gt;
        &lt;button
          className=<span class="hljs-string">"cursor-pointer outline-none"</span>
          aria-label=<span class="hljs-string">"Search"</span>
        &gt;
          &lt;FaSearch /&gt;
        &lt;/button&gt;
      &lt;/Popover.Trigger&gt;

      &lt;Popover.Portal&gt;

        &lt;Popover.Content
          className=<span class="hljs-string">"rounded p-2 bg-white dark:bg-gray-800 w-[480px] will-change-[transform,opacity] data-[state=open]:data-[side=top]:animate-slideDownAndFade data-[state=open]:data-[side=right]:animate-slideLeftAndFade data-[state=open]:data-[side=bottom]:animate-slideUpAndFade data-[state=open]:data-[side=left]:animate-slideRightAndFade"</span>
          sideOffset={<span class="hljs-number">5</span>}
        &gt;

          &lt;div className=<span class="hljs-string">'my-2'</span>&gt;
            &lt;label htmlFor=<span class="hljs-string">"default-search"</span> className=<span class="hljs-string">"mb-2 mt-5 text-sm font-medium text-gray-900 sr-only dark:text-white"</span>&gt;Search bar &lt;/label&gt;
            &lt;div className=<span class="hljs-string">"relative"</span>&gt;
              &lt;div className=<span class="hljs-string">"absolute inset-y-0 left-0 flex items-center pl-3 pointer-events-none"</span>&gt;
                &lt;svg className=<span class="hljs-string">"w-5 h-5 text-gray-500 dark:text-gray-400"</span> fill=<span class="hljs-string">"none"</span> stroke=<span class="hljs-string">"currentColor"</span> viewBox=<span class="hljs-string">"0 0 24 24"</span> xmlns=<span class="hljs-string">"http://www.w3.org/2000/svg"</span>&gt;&lt;path strokeLinecap=<span class="hljs-string">"round"</span> strokeLinejoin=<span class="hljs-string">"round"</span> strokeWidth=<span class="hljs-string">"2"</span> d=<span class="hljs-string">"M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z"</span>&gt;&lt;<span class="hljs-regexp">/path&gt;&lt;/</span>svg&gt;
              &lt;/div&gt;
              &lt;input <span class="hljs-keyword">type</span>=<span class="hljs-string">"search"</span> id=<span class="hljs-string">"default-search"</span> onChange={<span class="hljs-function">(<span class="hljs-params">event</span>) =&gt;</span> setQuery(event?.target.value)} className=<span class="hljs-string">"block w-full p-4 pl-10 text-sm text-gray-900 border border-gray-300 rounded-lg bg-gray-50 focus:ring-blue-500 focus:border-blue-500 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500"</span> placeholder=<span class="hljs-string">"Start searching here ..."</span> required /&gt;

            &lt;/div&gt;
          &lt;/div&gt;


          {

            serachPost.length &gt; <span class="hljs-number">0</span> ? serachPost.map(<span class="hljs-function"><span class="hljs-params">item</span> =&gt;</span> {

              <span class="hljs-keyword">return</span> (
                &lt;div key={item.uuid} className=<span class="hljs-string">'my-3'</span>&gt;
                  &lt;div className=<span class="hljs-string">"text-white my-2 py-2 bg-blue-400 dark:bg-gray-900 dark:hover:bg-blue-400 border-none rounded-md dark:text-white"</span>&gt;
                    &lt;Link href={<span class="hljs-string">`read/<span class="hljs-subst">${item.slug}</span>`</span>} className=<span class="hljs-string">"relative inline-flex items-center rounded-lg w-full px-4 py-2 text-sm font-medium"</span>&gt;
                      {item.title}
                    &lt;/Link&gt;
                  &lt;/div&gt;
                &lt;/div&gt;
              )
            }) : <span class="hljs-string">" "</span>

          }

        &lt;/Popover.Content&gt;

      &lt;/Popover.Portal&gt;

    &lt;/Popover.Root &gt;
  )
}

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> Search;
</code></pre>
<h3 id="heading-error-handling">Error Handling</h3>
<p>Next.js has two types of <a target="_blank" href="https://beta.nextjs.org/docs/routing/error-handling#how-errorjs-works">error handling</a>. the first is layout-based, and the second is <a target="_blank" href="https://beta.nextjs.org/docs/routing/error-handling#handling-errors-in-root-layouts">global error</a> handling. For the demo here, we'll use layout-based error handling.</p>
<p>Next provides a special type of <code>error.tsx</code> file to handle errors on your site. It does not handle 404, 500, and so on – it handles only runtime errors.</p>
<pre><code class="lang-typescript"><span class="hljs-string">'use client'</span>; <span class="hljs-comment">// Error components must be Client components</span>
<span class="hljs-keyword">import</span> React <span class="hljs-keyword">from</span> <span class="hljs-string">'react'</span>;
<span class="hljs-keyword">import</span> { useEffect } <span class="hljs-keyword">from</span> <span class="hljs-string">'react'</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">export</span> <span class="hljs-keyword">default</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">Error</span>(<span class="hljs-params">{ error, reset }: { error: <span class="hljs-built_in">Error</span>; reset: () =&gt; <span class="hljs-built_in">void</span>; }</span>) </span>{

  useEffect(<span class="hljs-function">() =&gt;</span> {
    <span class="hljs-built_in">console</span>.error(error);
  }, [error]);

  <span class="hljs-keyword">return</span> (
    &lt;section className=<span class="hljs-string">"dark:bg-gray-900 my-16"</span>&gt;

      &lt;div className=<span class="hljs-string">"py-8 px-4 mx-auto max-w-screen-xl lg:py-16 lg:px-6"</span>&gt;

        &lt;div className=<span class="hljs-string">"mx-auto max-w-screen-sm text-center"</span>&gt;

          &lt;h1 className=<span class="hljs-string">"mb-4 text-7xl tracking-tight font-extrabold lg:text-9xl text-primary-600 dark:text-primary-500"</span>&gt;Something wrong&lt;/h1&gt;
          &lt;p className=<span class="hljs-string">"mb-4 text-lg p-2 font-light bg-red-500 text-white dark:bg-red-400 dark:text-white"</span>&gt;{error.message}&lt;/p&gt;

          &lt;div className=<span class="hljs-string">'flex justify-around mt-2'</span>&gt;

            &lt;Link href=<span class="hljs-string">"#"</span> className=<span class="hljs-string">"inline-flex bg-gray-600 text-white hover:bg-gray-700 focus:ring-4 font-medium rounded-lg text-sm p-2
                text-center"</span>&gt;Back to Homepage&lt;/Link&gt;

            &lt;button className=<span class="hljs-string">'bg-gray-600 text-white rounded-lg p-2'</span> onClick={<span class="hljs-function">() =&gt;</span> reset()}&gt;
              Try again
            &lt;/button&gt;


          &lt;/div&gt;

        &lt;/div&gt;

      &lt;/div&gt;

    &lt;/section&gt;
  );
}
</code></pre>
<h4 id="heading-how-to-handle-404-errors">How to handle 404 errors</h4>
<p>To handle 404 errors in the Next.js app folder, you need to create a <code>not-found.tsx</code> file in your root level. </p>
<p>Our 404 file looks like this:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/04/nextjsandghosterror.png" alt="404 error" width="600" height="400" loading="lazy">
<em>404 error</em></p>
<p>Here's the code for that:</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">import</span> Link <span class="hljs-keyword">from</span> <span class="hljs-string">"next/link"</span>

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">NotFound</span>(<span class="hljs-params"></span>) </span>{

  <span class="hljs-keyword">return</span> (
    &lt;section className=<span class="hljs-string">"bg-white dark:bg-gray-900 my-16"</span>&gt;
      &lt;div className=<span class="hljs-string">"py-8 px-4 mx-auto max-w-screen-xl lg:py-16 lg:px-6"</span>&gt;
        &lt;div className=<span class="hljs-string">"mx-auto max-w-screen-sm text-center"</span>&gt;
          &lt;h1 className=<span class="hljs-string">"mb-4 text-7xl tracking-tight lg:text-9xl text-primary-600 dark:text-primary-500"</span>&gt;<span class="hljs-number">404</span>&lt;/h1&gt;
          &lt;p className=<span class="hljs-string">"mb-4 text-3xl tracking-tight font-bold text-gray-900 md:text-4xl dark:text-white"</span>&gt; Something wrong&lt;/p&gt;
          &lt;p className=<span class="hljs-string">"mb-4 text-lg font-light text-gray-500 dark:text-gray-400"</span>&gt;
            Sorry, we cant find that article. You will find lots to explore on the home page.
          &lt;/p&gt;
          &lt;Link href=<span class="hljs-string">"/"</span> className=<span class="hljs-string">"inline-flex text-white bg-black dark:bg-white dark:text-black p-3 hover:bg-gray-800 my-4"</span>&gt;Back to Homepage&lt;/Link&gt;
        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/section &gt;
  )

}

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> NotFound
</code></pre>
<p>The big issue with the <code>not-found.tsx</code> error file is that it doesn't show automatically in Next (v13.3.0). To show a 404 error, you need to show the error manually. Here's how you do that:</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">import</span> { notFound } <span class="hljs-keyword">from</span> <span class="hljs-string">'next/navigation'</span>;

<span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">Read</span>(<span class="hljs-params">{ params }: { params: { slug: <span class="hljs-built_in">string</span> }; }</span>) </span>{

  <span class="hljs-keyword">const</span> getPost = <span class="hljs-keyword">await</span> getSinglePost(params.slug)

  <span class="hljs-comment">// if not found getPost, then show 404 error</span>

  <span class="hljs-keyword">if</span> (!getPost) {
    notFound()
  }

  <span class="hljs-keyword">return</span> (
      &lt;main className=<span class="hljs-string">"pt-8 pb-16 lg:pt-16 lg:pb-24 dark:bg-gray-900"</span>&gt;

          rest <span class="hljs-keyword">of</span> code ....

      &lt;/main&gt;
      )
}
</code></pre>
<h3 id="heading-how-to-rebuild-your-static-site-with-webhooks">How to Rebuild Your Static Site with Webhooks</h3>
<p>The biggest problem when you create a static site happens if somebody writes a new post or changes an existing post in Ghost. For a personal project, you can manually redeploy your site. But for a larger site, you won't be able to do that every time this happens.</p>
<p>The best solution is to use webhooks. Ghost provides webhook support. If you update an existing post or write a new one, it'll update in Ghost. </p>
<p>In the demo project, we're using Vercel webhooks to deploy our blog. When we create a new blog or update something on the site, Ghost triggers the Vercel webhook. Then Vercel rebuilds the site as needed.</p>
<p>You do not need to write the code for this – just follow along and copy-paste as you go.</p>
<h4 id="heading-how-to-get-the-webhook-from-vercel">How to get the webhook from Vercel</h4>
<p>Firstly, go to the Vercel dashboard.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/04/select1.png" alt="Vercel dashboard" width="600" height="400" loading="lazy">
<em>Vercel dashboard</em></p>
<p>Select your project, where you'll deploy your Ghost frontend.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/04/select2.png" alt="Select the project in your vercel dashboard" width="600" height="400" loading="lazy">
<em>Select the project in your Vercel dashboard</em></p>
<p>Click on the settings tab in your Vercel project.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/04/select3.png" alt="Click the Git tab" width="600" height="400" loading="lazy">
<em>Click the Git tab</em></p>
<p>Then click on the Git tab. After scrolling down, you can see the deploy hook selection.  </p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/04/select4.png" alt="Go to Deploy hooks sections" width="600" height="400" loading="lazy">
<em>Go to Deploy hooks sections</em></p>
<p>Enter your webhook name and branch name and click on the "create hook" button.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/04/select5.png" alt="Copy your webhook url" width="600" height="400" loading="lazy">
<em>Copy your webhook url</em></p>
<p>Click on the copy button to copy your vercel webhook. </p>
<h4 id="heading-how-to-integrate-vercel-webhooks-in-the-ghost-dashboard">How to integrate Vercel webhooks in the Ghost dashboard</h4>
<p>When something changes in Ghost, it triggers the Vercel webhook URL. Then Vercel redeploys the blog site. </p>
<p>To integrate the Vercel webhook with Ghost, just follow these steps:</p>
<p>Open the Ghost CMS dashboard.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/04/ghost1.png" alt="Image" width="600" height="400" loading="lazy">
<em>Ghost dashboard</em></p>
<p>Click on the setting icon.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/04/ghost3.png" alt="Image" width="600" height="400" loading="lazy">
<em>Ghost settings</em></p>
<p>Click on the New custom integration button.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/04/ghost4.png" alt="Image" width="600" height="400" loading="lazy">
<em>Add a new custom integration</em></p>
<p>Enter the integration name.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/04/ghost5.png" alt="Image" width="600" height="400" loading="lazy">
<em>Add integration name</em></p>
<p>Click to add the webhook button.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/04/ghost7.png" alt="Image" width="600" height="400" loading="lazy">
<em>How to add the webhook</em></p>
<p>First, enter the name, then select Event and paste the URL which you copied from the Vercel dashboard.</p>
<p>Based on the event, Ghost will call the webhook, and your website will rebuild. Redeploys take time based on how big your site is, and so on.</p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>Everything should work well using Next.js and the Ghost CMS as we've worked through in this tutorial.</p>
<p>But some of the Ghost editor components, like toggles, where you need JavaScript interaction, don't work. You can solve this by writing your own JavaScript or getting a JavaScript file for Ghost and adding it to the <code>read/[slug]/page.tsx</code> file.  </p>
<p>You can save a lot of money on hosting by combining Next.js and the Ghost CMS API, but you lose some features like inbuilt signup, login, accounts, subscriptions, search bar, and member access levels.</p>
<p>You can share and follow me on <a target="_blank" href="https://twitter.com/Official_R_deep">Twitter</a> and <a target="_blank" href="https://www.linkedin.com/in/officalrajdeepsingh/">Linkedin</a>. If you like my work, you can read more content on my blog, the <a target="_blank" href="https://officialrajdeepsingh.dev/">officialrajdeepsingh.dev</a>, <a target="_blank" href="https://medium.com/frontendweb">frontend web</a>, and Sign up for my <a target="_blank" href="https://officialrajdeepsingh.medium.com/subscribe">free newsletter</a>.</p>
<p>You can also check out <a target="_blank" href="https://github.com/officialrajdeepsingh/awesome-nextjs">awesome-next</a>, a curated list of awesome Nextjs-based libraries that help build small and large-scale applications with Next.js.</p>
<p>Here are some additional resources you can use if you need more help or information while going through this tutorial:</p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://ghost.org/docs/jamstack/next/">https://ghost.org/docs/jamstack/next/</a></div>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://www.digitalocean.com/community/tutorials/how-to-build-your-blog-on-digitalocean-with-ghost-and-next-js">https://www.digitalocean.com/community/tutorials/how-to-build-your-blog-on-digitalocean-with-ghost-and-next-js</a></div>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://ghost.org/docs/content-api/">https://ghost.org/docs/content-api/</a></div>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://beta.nextjs.org/docs/getting-started">https://beta.nextjs.org/docs/getting-started</a></div>
<p>I write tons of articles on Next. If you are interested in Next and related stuff, you can follow me on <a target="_blank" href="https://officialrajdeepsingh.medium.com/">Medium</a> and join the <a target="_blank" href="https://medium.com/frontendweb">frontend web publication</a>.  </p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Build a Custom Ghost CMS Theme ]]>
                </title>
                <description>
                    <![CDATA[ Ghost CMS is a platform specifically designed for bloggers and writers. Using Ghost, you can quickly get a blog website up and running.  Ghost targets primarily writers and all the features are specifically built for writing.  Ghost's new dashboard g... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-build-a-ghost-cms-theme/</link>
                <guid isPermaLink="false">66d038a8d6e6a35e9f8e6978</guid>
                
                    <category>
                        <![CDATA[ blog ]]>
                    </category>
                
                    <category>
                        <![CDATA[ cms ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Ghost ]]>
                    </category>
                
                    <category>
                        <![CDATA[ projects ]]>
                    </category>
                
                    <category>
                        <![CDATA[ writing ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Rajdeep Singh ]]>
                </dc:creator>
                <pubDate>Wed, 04 Jan 2023 15:20:24 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2022/08/ghost-theme-development-.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Ghost CMS is a platform specifically designed for bloggers and writers. Using Ghost, you can quickly get a blog website up and running. </p>
<p>Ghost targets primarily writers and all the features are specifically built for writing. </p>
<p>Ghost's new dashboard gives you a user-friendly interface, and beginners can easily understand the functionality. In addition, Ghost's free tutorials will help you if you have any problems. </p>
<p><strong>Some cool Ghost features:</strong></p>
<ol>
<li>Open-source</li>
<li>Membership support</li>
<li>Rich text editor (Koenig editor)</li>
<li>Newsletters</li>
<li>Email subscriber support</li>
<li>Login functionality support</li>
<li>Integration plugin </li>
<li>Analytics dashboard</li>
<li>Inbuilt comment support</li>
<li>Inbuilt search support</li>
<li>Inbuilt search functionality</li>
<li>SEO and different types of metadata support social media</li>
<li>Custom theme design</li>
</ol>
<p>You can check out all <a target="_blank" href="https://github.com/frontendweb3/fastest">the code available for this project on GitHub here</a>.</p>
<h2 id="heading-heres-what-well-cover">Here's what we'll cover:</h2>
<ol>
<li><a class="post-section-overview" href="#heading-self-hosting-vs-hosting-with-ghost">Self-Hosting vs Hosting with Ghost</a></li>
<li><a class="post-section-overview" href="#heading-what-are-the-drawbacks-of-ghost-cms">What Are the Drawbacks of Ghost?</a></li>
<li><a class="post-section-overview" href="#heading-how-to-install-the-ghost-cms">How to Install the Ghost CMS</a></li>
<li><a class="post-section-overview" href="#heading-understanding-the-ghost-folder-structure">Understanding the Ghost Folder Structure</a></li>
<li><a class="post-section-overview" href="#heading-understanding-the-ghost-theme-folder-structure">Understanding the Ghost Theme Folder Structure</a></li>
<li><a class="post-section-overview" href="#heading-how-to-create-a-new-theme-with-the-npm-cli-tool">How to Create a New Theme with the npm CLI Tool</a></li>
<li><a class="post-section-overview" href="#heading-how-to-create-a-new-ghost-theme-from-scratch">How to Create a New Ghost Theme from Scratch</a></li>
<li><a class="post-section-overview" href="##how-to-install-ghost-cli-globally">How to Install ghost-cli Globally</a></li>
<li><a class="post-section-overview" href="#heading-how-to-install-ghost-locally">How to install Ghost locally</a></li>
<li><a class="post-section-overview" href="#heading-how-to-configure-tailwind-css">How to Configure Tailwind CSS</a></li>
<li><a class="post-section-overview" href="#heading-other-important-commands-in-the-ghost-cli">Other Important Commands in the Ghost CLI</a></li>
<li><a class="post-section-overview" href="#how-to-write-the-code-for-our-custom-ghost-theme">How to Write the Code for Our Custom Ghost Theme</a></li>
<li><a class="post-section-overview" href="#heading-how-to-add-theme-configuration-in-packagejson">How to add theme configuration in package.json</a></li>
<li><a class="post-section-overview" href="#heading-how-to-use-theme-helpers">How to Use Theme Helpers</a></li>
<li><a class="post-section-overview" href="#heading-what-is-the-partials-folder">What is the Partials Folder?</a></li>
<li><a class="post-section-overview" href="#heading-how-to-create-a-default-page">How to Create a Default Page</a></li>
<li><a class="post-section-overview" href="#heading-how-to-create-an-index-page">How to Create an Index Page</a></li>
<li><a class="post-section-overview" href="#heading-how-to-create-a-posts-page">How to Create a Posts Page</a></li>
<li><a class="post-section-overview" href="#heading-how-to-create-informational-pages">How to Create Information Pages</a></li>
<li><a class="post-section-overview" href="#heading-how-to-create-an-author-page">How to Create an Author Page</a></li>
<li><a class="post-section-overview" href="#heading-how-to-create-a-tags-page">How to Create a Tags Page</a></li>
<li><a class="post-section-overview" href="#heading-how-to-create-an-error-page">How to Create an Error Page</a></li>
<li><a class="post-section-overview" href="#heading-how-to-enable-comments">How to Enable Comments</a></li>
<li><a class="post-section-overview" href="#heading-how-to-set-up-search">How to Set Up Search</a></li>
<li><a class="post-section-overview" href="#heading-conclusion">Conclusion</a></li>
</ol>
<h2 id="heading-self-hosting-vs-hosting-with-ghost">Self-hosting vs Hosting with Ghost</h2>
<p>Ghost provides two ways to create/host your website:</p>
<ol>
<li>Self-hosting</li>
<li>With the Ghost cloud platform</li>
</ol>
<h3 id="heading-self-hosting">Self-hosting</h3>
<p>If you choose to self-host, you'll host your website on any cloud platform like <a target="_blank" href="https://cloud.google.com/">Google cloud</a>, <a target="_blank" href="https://aws.amazon.com/">AWS cloud,</a> <a target="_blank" href="https://azure.microsoft.com/">Azure cloud</a>, <a target="_blank" href="https://www.digitalocean.com/">Digital Ocean</a>, and so on. These are some of the most used cloud platforms in the market. </p>
<p>Most cloud platforms come with one click to deploy solutions. This means you can deploy your Ghost blog with a single click. </p>
<p>Before deploying your Ghost blog, you should compare all cloud platforms based on pricing before choosing one.  </p>
<p>Self-hosting your Ghost blog is free, and you do not need to pay anything to the Ghost platform. You'll just pay your cloud provider. </p>
<h3 id="heading-hosting-with-the-ghost-cloud-platform">Hosting with the Ghost Cloud Platform</h3>
<p>If you choose to host with Ghost, they'll help create the blog and host it on the Ghost platform itself. The Ghost team handles all the maintenance and security. You won't have to worry about updating Ghost and any themes you're using – the Ghost staff will handle that for you.</p>
<p>Self-hosting focuses more on developers, while hosting with the Ghost platform targets anyone who doesn't know about computers and programming.</p>
<p>Ghost hosting comes with a paid plan – it's not free. But they give you 14 day free trial period, after which you shift automatically into a paid plan.</p>
<h3 id="heading-what-should-you-choose-paid-or-self-hosting">What should you choose, paid or self-hosting?</h3>
<p>In my experience, hosting with the Ghost platform is the best solution for beginner developers, non-developers, and writers. The Ghost team handles everything for you. You do not worry about traffic, security, or maintenance and do not need to update the Ghost CMS. This lets you focus on writing. </p>
<p>As a developer, I always recommended that you self-host Ghost. I have run my self-hosted Ghost blog with Google Cloud for two years with a Bitnami one-click deployment.</p>
<p>After six months, I'd used up my $200 free credit, and then I started to pay monthly to Google Cloud hosting.</p>
<p>For a non-technical person, I highly recommended using the Ghost (pro) cloud platform and as well any other platform that provides Ghost-based cloud and shares hosting.</p>
<p>I found a <a target="_blank" href="https://geekflare.com/ghost-hosting-platforms/">list of Ghost-hosting platforms</a> on the internet. Perhaps one of these will solve your hosting issues or questions. If you plan to deploy Ghost with the G<a target="_blank" href="https://officialrajdeepsingh.dev/tags/ghost-cms/">oogle Cloud platform</a>, I have an article on that.</p>
<h2 id="heading-what-are-the-drawbacks-of-ghost-cms">What are the Drawbacks of Ghost CMS?</h2>
<p>The biggest drawback of Ghost is that web performance can feel slow. If you want good web performance, you'll likely need to use a CDN for media (images, videos, and PDFs) and also for CSS and JavaScript.  </p>
<p>The second biggest drawback is cost. I've been running my blog with Ghost for two years, and I pay 10 to 20 times extra to Google Cloud for hosting as a self-deploy. </p>
<p>My website has 4000 to 5000 active monthly users, and I pay 20 times extra. Because of this, I shifted my website to Hugo. </p>
<p>Now I still have 4000 to 5000 active users on the website, and I pay zero money to Netlify.</p>
<h3 id="heading-the-solution-for-developers">The Solution for Developers</h3>
<p>The best solution for developers is to use Ghost as a backend and, with the REST API, choose any JavaScript framework like Next.js, Fresh, Astro, and so on. </p>
<p>There are a lot of frameworks that can help you build a static website. In addition, static websites are fast and deploy with zero JavaScript.</p>
<p>In this method, you may not use all Ghost's features, but you can save a lot of money. Still, building the website with a JavaScript framework takes a lot of time just to run the essential version of the website. </p>
<p>My solution only works well for a small team. So if your team has a lot of writers and submits many articles in a single day, I'd recommend sticking with Ghost CMS as a frontend and backend.</p>
<p>Ghost version 5.0 is 20% faster than the old version. Suppose you use Ghost and want to design your own custom theme – then this article is for you. Let's get started.</p>
<h2 id="heading-how-to-install-the-ghost-cms">How to Install the Ghost CMS</h2>
<p>How you install Ghost CMS changes according to your operating system. We'll discuss installation for all operating systems in this guide. You can install Ghost with npm, yarn, and Docker.</p>
<p>Now let's look at how to install Ghost for:</p>
<ol>
<li>Windows, Linux, and macOS</li>
<li>Docker image</li>
</ol>
<h2 id="heading-how-to-install-ghost-on-windows-linux-and-macos">How to Install Ghost on Windows, Linux, and macOS</h2>
<p>Setting up the Ghost theme development environment in Windows and macOS is a straightforward process. But it's best if you've installed the npm or yarn package manager. If you don't have Node.js, npm, and yarn, yolu'll need to install them – Node.js comes with preinstalled npm and yarn. </p>
<p>To install Ghost CMS globally and locally, follow these basic steps:</p>
<h3 id="heading-how-to-install-ghost-cli-globally">How to install ghost-CLI globally</h3>
<p>First, you can install <code>ghost-cli</code> globally on your machine using npm or yarn. Here are the commands:</p>
<pre><code class="lang-bash">npm install ghost-cli@latest -g
    OR
yarn global add ghost-cli@latest
</code></pre>
<h3 id="heading-how-to-install-ghost-locally">How to install Ghost locally</h3>
<p>Next, when your ghost-CLI installation is complete, then run the <code>ghost local</code> command in your terminal. It looks like this:</p>
<pre><code class="lang-bash">ghost install <span class="hljs-built_in">local</span>
</code></pre>
<p>The command output looks like this:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/09/carbon--1-.png" alt="Ghost cms folder structure" width="600" height="400" loading="lazy">
<em>Ghost cms folder structure</em></p>
<p><strong>Note</strong>: you'll need to run the <code>ghost install local</code> command in an empty folder. Otherwise, you'll face an error:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/09/ghost-not-empty-error.png" alt="Not empty directory error with ghost-cli" width="600" height="400" loading="lazy">
<em>Not empty directory error with ghost-cli</em></p>
<p>To start the local development server, run the <code>ghost start</code> command in your terminal.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/09/ghost-start-command.png" alt="Ghost start command " width="600" height="400" loading="lazy">
<em>Ghost start command</em></p>
<p>If you get an error when running <code>ghost start</code> in Ubuntu, run the following command: <code>ghost start --no-setup-linux-user</code>.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/09/not-readable-by-other-user.png" alt="The directory is not readable by other users' errors in ghost cms." width="600" height="400" loading="lazy">
<em>The directory is not readable by other users' error in Ghost CMS.</em></p>
<h2 id="heading-how-to-set-up-your-environment-using-a-docker-image">How to Set Up Your Environment Using a Docker Image</h2>
<p>Docker is also a great way to set up a theme development or production environment for Ghost. The Ghost team provides an official <a target="_blank" href="https://hub.docker.com/_/ghost">Ghost Docker image</a> on Docker Hub. </p>
<p>To start the setup, you'll need the <code>docker-compose.yml</code> file in your root project folder. Then run the <code>docker-compose up</code> command in your terminal. </p>
<pre><code>version: <span class="hljs-string">'3.8'</span>
<span class="hljs-attr">services</span>:
  blog:
    image: ghost
    <span class="hljs-attr">restart</span>: always
    <span class="hljs-attr">ports</span>:
      - <span class="hljs-number">8080</span>:<span class="hljs-number">2368</span>
    <span class="hljs-attr">volumes</span>:
      - ./custom-ghost-theme:<span class="hljs-regexp">/var/</span>lib/ghost/content/themes/custom-ghost-theme/
    environment:
      url: http:<span class="hljs-comment">//localhost:808</span>
      NODE_ENV: development
</code></pre><p>In the volume section, you pass your file. In my case, I added a specific file in my Ghost theme folder.</p>
<pre><code>version: <span class="hljs-string">'3.8'</span>
<span class="hljs-attr">services</span>:
  blog:
    image: ghost
    <span class="hljs-attr">restart</span>: always
    <span class="hljs-attr">ports</span>:
      - <span class="hljs-number">8080</span>:<span class="hljs-number">2368</span>
    <span class="hljs-attr">volumes</span>:
      - ./assets:<span class="hljs-regexp">/var/</span>lib/ghost/content/themes/fastest/assets
      - ./partials:<span class="hljs-regexp">/var/</span>lib/ghost/content/themes/fastest/partials
      - ./author.hbs:<span class="hljs-regexp">/var/</span>lib/ghost/content/themes/fastest/author.hbs
      - ./<span class="hljs-keyword">default</span>.hbs:<span class="hljs-regexp">/var/</span>lib/ghost/content/themes/fastest/<span class="hljs-keyword">default</span>.hbs
      - ./error<span class="hljs-number">-404.</span>hbs:<span class="hljs-regexp">/var/</span>lib/ghost/content/themes/fastest/error<span class="hljs-number">-404.</span>hbs
      - ./error.hbs:<span class="hljs-regexp">/var/</span>lib/ghost/content/themes/fastest/error.hbs
      - ./gulpfile.js:<span class="hljs-regexp">/var/</span>lib/ghost/content/themes/fastest/gulpfile.js
      - ./index.hbs:<span class="hljs-regexp">/var/</span>lib/ghost/content/themes/fastest/index.hbs
      - ./package-lock.json:<span class="hljs-regexp">/var/</span>lib/ghost/content/themes/fastest/package-lock.json
      - ./package.json:<span class="hljs-regexp">/var/</span>lib/ghost/content/themes/fastest/package.json
      - ./page.hbs:<span class="hljs-regexp">/var/</span>lib/ghost/content/themes/fastest/page.hbs
      - ./post.hbs:<span class="hljs-regexp">/var/</span>lib/ghost/content/themes/fastest/post.hbs
      - ./query.hbs:<span class="hljs-regexp">/var/</span>lib/ghost/content/themes/fastest/query.hbs
      - ./tag.hbs:<span class="hljs-regexp">/var/</span>lib/ghost/content/themes/fastest/tag.hbs
      - ./readme.md:<span class="hljs-regexp">/var/</span>lib/ghost/content/themes/fastest/readme.md
    <span class="hljs-attr">environment</span>:
      url: http:<span class="hljs-comment">//localhost:8080</span>
      NODE_ENV: development
</code></pre><p>In your custom-ghost-theme folder, you need the <code>index.hbs</code>, <code>post.hbs</code>, and <code>package.json</code> files to start theme development. But, you'll get an error when you activate your theme in the Ghost dashboard without requiring a file.</p>
<p><strong>Here's the GitHub repo if you want to follow along:</strong></p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://github.com/officialrajdeepsingh/ghostthemewithdocker">https://github.com/officialrajdeepsingh/ghostthemewithdocker</a></div>
<h4 id="heading-errors">Errors</h4>
<p>In Ubuntu (22.04) or any other Linux distros, you'll get the <code>Message: The directory /home/rajdeepsingh/ is not readable by other users on the system</code> error. This means yours is old. So update your <code>ghost-cli</code> then run the <code>ghost start</code> command in your folder.</p>
<h2 id="heading-understanding-the-ghost-folder-structure">Understanding the Ghost Folder Structure</h2>
<p>The Ghost folder structure has three main folders and one file. They are:</p>
<ol>
<li>The <code>config.development.json</code> file contains the configuration for Ghost development.</li>
<li>The <code>current</code> folder is a link (symbolic link) that targets the install version.</li>
<li>The <code>version</code> folder contains all versions of Ghost cms.</li>
<li>The content folder is the main folder containing our database file, settings, theme, images, media, and so on.</li>
</ol>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/09/ghost-folder-sturture.png" alt="Ghost cms folder Structure" width="600" height="400" loading="lazy">
<em>Ghost CMS folder Structure</em></p>
<p>The folder structure might change according to the operating system but the <code>content</code> folder is the same in every operating system.</p>
<p>The content folder contains all the important files for Ghost.  They are:</p>
<ol>
<li>The data folder contains an SQLite3 database file. Ghost, by default, uses the SQLite3 database.</li>
<li>Files, images, and media folders contain all files which writers upload.</li>
<li>The public folder contains all public CSS and JavaScript files – for example, card and member JavaScript and CSS files.</li>
<li>Finally, the settings folder contains all the settings, for example, the <code>router.xml</code> file.</li>
<li>The theme folder contains all files and folders used to develop the theme.</li>
</ol>
<h2 id="heading-understanding-the-ghost-theme-folder-structure">Understanding the Ghost Theme Folder Structure</h2>
<p>You can build a new custom theme store in the <code>content/theme</code> folder. To develop a new theme, you'll always need to create a new folder with the theme name and store all files in the theme name folder.</p>
<pre><code><span class="hljs-comment">// theme structure</span>

content 
content/theme
content/theme/my-theme-name
content/theme/my-theme-name/index.hbs
content/theme/my-theme-name/post.hbs
content/theme/my-theme-name/package.json

<span class="hljs-comment">// rest of file created in my-theme-name folder</span>
</code></pre><p>Ghost CMS uses <strong>handlebars</strong> to build a Ghost theme. There are a number of files but only three files are required:</p>
<ol>
<li><code>index.hbs</code> in the main file (required) to design the home page of the website.</li>
<li><code>post.hbs</code> the file (required)  is used to read and design the full article.</li>
<li><code>package.json</code> file (required)  is used for Node.js config, and it also uses the theme name, description, version, custom config, and so on.</li>
<li>The <code>default.hbs</code> file is used to build the layout of the theme.</li>
<li>The assets folder contains all the JavaScript, CSS, fonts, and image files.</li>
<li>The partials folder helps divide files into small partials (parts) for better code readability.</li>
</ol>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/09/theme-struture.png" alt="Ghost theme folder structure" width="600" height="400" loading="lazy">
<em>Ghost theme folder structure</em></p>
<h2 id="heading-how-to-create-a-new-theme-with-the-npm-cli-tool">How to Create a New Theme with the npm CLI Tool</h2>
<p>The easiest way to start a new Ghost theme is with the <a target="_blank" href="https://www.npmjs.com/package/create-ghost-theme">create-ghost-theme CLI</a>. I built it, and I maintain it. The create-ghost-theme CLI helps you create the following folder structure that we'll discuss next. But currently, it only supports <strong>Tailwind CSS</strong>. </p>
<p>First, we'll create a new theme with the create-ghost-theme CLI and restart the Ghost CMS local server again.</p>
<h3 id="heading-folder-structure">Folder structure</h3>
<p>After creating a new theme with <a target="_blank" href="https://www.npmjs.com/package/create-ghost-theme">create-ghost-theme CLI</a>, your folder structure looks like this:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/09/ghost-theme-cli.png" alt="create-ghost-theme cli folder structure" width="600" height="400" loading="lazy">
<em>create-ghost-theme cli folder structure</em></p>
<h3 id="heading-understanding-the-new-website-layout">Understanding the New Website Layout</h3>
<p>After creating the theme with create-ghost-theme CLI, your theme looks like this. </p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/09/default-hbs.png" alt="Image" width="600" height="400" loading="lazy">
<em>index.hbs</em></p>
<p>Your website reading page will look like this:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/09/index-hbs.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>Your new tag page looks like this:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/09/tag-hbs.png" alt="Image" width="600" height="400" loading="lazy"></p>
<h2 id="heading-how-to-create-a-new-ghost-theme-from-scratch">How to Create a New Ghost Theme from Scratch</h2>
<p>When you're learning about Ghost theme development, my recommendation is to start creating a new theme from scratch. Then you can use the CLI tool I just showed you. This will be a lot easier for you.</p>
<p>So now, that's what we're going to cover in-depth: how to create a new Ghost CMS theme from scratch. </p>
<h3 id="heading-requirements">Requirements:</h3>
<p>To create a new theme, you'll need two libraries: the first is <code>ghost-cli</code> and the second is Tailwind CSS. </p>
<p>Here's what we'll go over in the coming sections:</p>
<ol>
<li>How to install <code>Ghost-cli</code> globally</li>
<li>How to configure Tailwind CSS</li>
<li>How to understand more commands in the Ghost CLI</li>
<li>Finally, we'll write the code</li>
</ol>
<h3 id="heading-how-to-install-ghost-cli-globally-1">How to Install ghost-cli Globally</h3>
<p>We went over how to do this above, but in case you need a reminder here it is:</p>
<p>First, you can install <code>ghost-cli</code> globally on your machine using npm or yarn. Here are the commands:</p>
<pre><code class="lang-bash">npm install ghost-cli@latest -g
    OR
yarn global add ghost-cli@latest
</code></pre>
<h3 id="heading-how-to-configure-tailwind-css">How to Configure Tailwind CSS</h3>
<p>Tailwind CSS is a powerful CSS library for designing the front end of a website. And you can easily use it with Ghost.</p>
<p>Install Tailwind CSS in your theme folder like this:</p>
<pre><code class="lang-bash">npm install -D tailwindcss postcss autoprefixer
</code></pre>
<p>After Tailwind and another package have been successfully installed, then run the following command to configure Tailwind for your theme development:</p>
<pre><code class="lang-bash">npx tailwindcss init
</code></pre>
<p>The <code>tailwindcss init</code> command creates a <code>tailwind.config.js</code> file. Here's what you'll see:</p>
<pre><code class="lang-javascript"><span class="hljs-comment">/** <span class="hljs-doctag">@type <span class="hljs-type">{import('tailwindcss').Config}</span> </span>*/</span>
<span class="hljs-built_in">module</span>.exports = {
<span class="hljs-attr">content</span>: [],
  <span class="hljs-attr">theme</span>: {
    <span class="hljs-attr">extend</span>: {},
  },
  <span class="hljs-attr">plugins</span>: [],
}
</code></pre>
<p>Config your template path in the content section, so Tailwind CSS tracks the CSS classes. Then compile those classes in the production file.</p>
<pre><code class="lang-javascript"><span class="hljs-comment">/** <span class="hljs-doctag">@type <span class="hljs-type">{import('tailwindcss').Config}</span> </span>*/</span>
<span class="hljs-built_in">module</span>.exports = {
  <span class="hljs-attr">content</span>: [<span class="hljs-string">"*.hbs"</span>,<span class="hljs-string">"partials/*.hbs"</span>],
  <span class="hljs-attr">darkMode</span>: <span class="hljs-string">'class'</span>,
  <span class="hljs-attr">theme</span>: {
    <span class="hljs-attr">extend</span>: {},
  },
  <span class="hljs-attr">plugins</span>: [],
}
</code></pre>
<p>Create a <code>main.css</code> or <code>dev.css</code> and use any other file name to create the file for Tailwind directives. Then paste the following tailwind CSS directives code into the file:</p>
<pre><code class="lang-css"><span class="hljs-keyword">@tailwind</span> base;
<span class="hljs-keyword">@tailwind</span> components;
<span class="hljs-keyword">@tailwind</span> utilities;
</code></pre>
<p>Create the script for Tailwind CSS to check all the classes then create a production-ready CSS file for your theme. </p>
<pre><code class="lang-json">    <span class="hljs-string">"scripts"</span>: {
        <span class="hljs-attr">"start"</span>: <span class="hljs-string">"npx tailwindcss -i ./assets/css/dev.css -o ./assets/build/css/build.css --watch"</span>
    },
</code></pre>
<p>The last step is to link the production-ready CSS file to your theme like this:</p>
<pre><code class="lang-handlebars">&lt;head&gt;
    {{!-- link production ready css file  --}}
    &lt;link rel="stylesheet" href="{{asset 'build/css/build.css'}}" /&gt;
&lt;/head&gt;
</code></pre>
<p>The one problem you might face when you enable Tailwind CSS in a Ghost theme is that refreshing your site in the development process is manual. When you change anything related to Tailwind classes, you'll need manually refresh your website again. I haven't found a solution yet, but you can use the live server for that for now.</p>
<h3 id="heading-other-important-commands-in-the-ghost-cli">Other Important Commands in the Ghost CLI</h3>
<p>There are a number of other commands you'll use often when working in the Ghost CLI. Let's go through them now. Here's what we'll discuss:</p>
<ol>
<li>ghost stop</li>
<li>ghost ls</li>
<li>ghost doctor</li>
<li>ghost uninstall</li>
<li>ghost version</li>
<li>ghost restart</li>
<li>ghost update</li>
<li>ghost version</li>
<li>ghost --help</li>
</ol>
<h4 id="heading-how-to-use-the-ghost-stop-command">How to use the ghost stop command</h4>
<p>The <code>ghost stop</code> command stops the currently running instance.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/12/ghost-stop.png" alt="Image" width="600" height="400" loading="lazy"></p>
<h4 id="heading-how-to-use-the-ghost-ls-command">How to use the ghost ls command</h4>
<p>The <code>ghost ls</code> command prints the current installs instance list in your machine.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/12/ghost-ls.png" alt="Image" width="600" height="400" loading="lazy"></p>
<h4 id="heading-how-to-use-the-ghost-doctor-command">How to use the ghost doctor command</h4>
<p>The <code>ghost doctor</code> command checks the system's health to see if everything is fine before running the <code>ghost install</code> or <code>ghost update</code> command. </p>
<p>If you face any errors in Ghost, you can also use the ghost doctor command to check the errors.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/12/ghost-doctor.png" alt="Image" width="600" height="400" loading="lazy"></p>
<h4 id="heading-how-to-use-the-ghost-uninstall-command">How to use the ghost uninstall command</h4>
<p>The <code>ghost uninstall</code> command removes the Ghost instance and related configuration files as well.</p>
<h4 id="heading-how-to-check-the-ghost-version">How to check the Ghost version</h4>
<p>You can use the <code>ghost version</code> command to check your currently installed version of Ghost.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/12/ghost-version.png" alt="Image" width="600" height="400" loading="lazy"></p>
<h4 id="heading-how-to-use-the-ghost-restart-command">How to use the ghost restart command</h4>
<p>The <code>ghost restart</code> command restarts your currently running Ghost instance.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/12/ghost-restart.png" alt="Image" width="600" height="400" loading="lazy"></p>
<h4 id="heading-how-to-use-the-ghost-update-command">How to use the ghost update command</h4>
<p>The ghost update command updates your old Ghost version to the new version.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/12/ghost-update.png" alt="Image" width="600" height="400" loading="lazy"></p>
<h4 id="heading-how-to-use-the-ghost-help-command">How to use the ghost --help command</h4>
<p>The <code>ghost --help</code> command prints a help page:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/12/ghost---help.png" alt="Image" width="600" height="400" loading="lazy"></p>
<h3 id="heading-finally-well-write-the-code">Finally, we'll write the code</h3>
<p>Now we get to start writing the code. Here's what we'll cover in the coming sections:</p>
<ol>
<li>How to add theme configuration in <code>package.json</code> [required]</li>
<li>How to use theme helpers</li>
<li>What is the partials folder?</li>
<li>How to create a default page</li>
<li>How to create an index page [required]</li>
<li>How to create a posts page [required]</li>
<li>How to create informational page</li>
<li>How to create a tags page</li>
<li>How to set up comments</li>
<li>How to enable search</li>
</ol>
<h3 id="heading-how-to-add-theme-configuration-in-packagejson">How to add theme configuration in package.json</h3>
<p>The <code>package.json</code> file is the main file where you add or change the theme name, description, and custom configuration for the theme. </p>
<p>The <strong>first step</strong> is to create <code>package.json</code> file and add the theme name, description, version, and additional configuration. </p>
<p>The following properties are used by Ghost themes: <code>name</code>, <code>description</code>, <code>version</code>, <code>engines</code>, <code>card_assets</code>, <code>license</code>, <code>author</code>, <code>keywords</code>, <code>screenshots</code>, and <code>config</code> in the <code>package.json</code> file. </p>
<p>The most important properties are <code>name</code>, <code>description</code>, <code>version</code>,  <code>engines</code>, <code>card_assets</code>, and <code>config</code>. Here's what this looks like in the code:</p>
<pre><code class="lang-json">{
    <span class="hljs-attr">"name"</span>: <span class="hljs-string">"fastest"</span>,
    <span class="hljs-attr">"description"</span>: <span class="hljs-string">"Fastest ghost cms base theme. Fastest is light weight, modern open-source theme"</span>,
    <span class="hljs-attr">"version"</span>: <span class="hljs-string">"1.0.7"</span>,
    <span class="hljs-attr">"engines"</span>: {
        <span class="hljs-attr">"ghost"</span>: <span class="hljs-string">"&gt;=5.0.0"</span>
    },
    <span class="hljs-attr">"license"</span>: <span class="hljs-string">"MIT"</span>,
    <span class="hljs-attr">"scripts"</span>: {
        <span class="hljs-attr">"dev"</span>: <span class="hljs-string">"gulp"</span>,
        <span class="hljs-attr">"start"</span>: <span class="hljs-string">"npx tailwindcss -i ./assets/css/dev.css -o ./assets/build/css/build.css --watch"</span>
    },
    <span class="hljs-attr">"author"</span>: {
        <span class="hljs-attr">"name"</span>: <span class="hljs-string">"Rajdeep Singh"</span>,
        <span class="hljs-attr">"email"</span>: <span class="hljs-string">"officialrajdeepsingh@gmail.com"</span>,
        <span class="hljs-attr">"url"</span>: <span class="hljs-string">"https://officialrajdeepsingh.dev"</span>
    },
    <span class="hljs-attr">"keywords"</span>: [
        <span class="hljs-string">"ghost"</span>,
        <span class="hljs-string">"theme"</span>,
        <span class="hljs-string">"blog"</span>,
        <span class="hljs-string">"light weight"</span>,
        <span class="hljs-string">"ghost-theme"</span>
    ],
    <span class="hljs-attr">"screenshots"</span>: {
        <span class="hljs-attr">"desktop"</span>: [
            <span class="hljs-string">"assets/dark.png"</span>,
            <span class="hljs-string">"assets/light.png"</span>
        ],
        <span class="hljs-attr">"mobile"</span>: <span class="hljs-string">"assets/mobile.png"</span>
    },
    <span class="hljs-attr">"config"</span>: {
        <span class="hljs-attr">"posts_per_page"</span>: <span class="hljs-number">10</span>,
        <span class="hljs-attr">"card_assets"</span>: <span class="hljs-literal">true</span>,
        <span class="hljs-attr">"image_sizes"</span>: {},
        <span class="hljs-attr">"custom"</span>: {
            <span class="hljs-attr">"linkedin_url"</span>: {
                <span class="hljs-attr">"type"</span>: <span class="hljs-string">"text"</span>,
                <span class="hljs-attr">"default"</span>: <span class="hljs-string">"None"</span>
            },
            <span class="hljs-attr">"github_url"</span>: {
                <span class="hljs-attr">"type"</span>: <span class="hljs-string">"text"</span>,
                <span class="hljs-attr">"default"</span>: <span class="hljs-string">"None"</span>
            },
            <span class="hljs-attr">"instagram_url"</span>: {
                <span class="hljs-attr">"type"</span>: <span class="hljs-string">"text"</span>,
                <span class="hljs-attr">"default"</span>: <span class="hljs-string">"None"</span>
            },
            <span class="hljs-attr">"copyright"</span>: {
                <span class="hljs-attr">"type"</span>: <span class="hljs-string">"text"</span>,
                <span class="hljs-attr">"default"</span>: <span class="hljs-string">"Copy right by Rajdeep Singh"</span>
            },
            <span class="hljs-attr">"copyright_url"</span>: {
                <span class="hljs-attr">"type"</span>: <span class="hljs-string">"text"</span>,
                <span class="hljs-attr">"default"</span>: <span class="hljs-string">"https://officialrajdeepsingh.dev/pages/terms-and-conditions/"</span>
            },
            <span class="hljs-attr">"adsense_enable"</span>: {
                <span class="hljs-attr">"type"</span>: <span class="hljs-string">"select"</span>,
                <span class="hljs-attr">"options"</span>: [
                    <span class="hljs-string">"Disable"</span>,
                    <span class="hljs-string">"Enable"</span>
                ],
                <span class="hljs-attr">"default"</span>: <span class="hljs-string">"Disable"</span>
            }
        }
        },
    <span class="hljs-attr">"devDependencies"</span>: {
        <span class="hljs-attr">"@tailwindcss/typography"</span>: <span class="hljs-string">"^0.5.8"</span>,
        <span class="hljs-attr">"autoprefixer"</span>: <span class="hljs-string">"^10.4.13"</span>,
        <span class="hljs-attr">"cssnano"</span>: <span class="hljs-string">"^5.0.17"</span>,
        <span class="hljs-attr">"gscan"</span>: <span class="hljs-string">"^4.22.0"</span>,
        <span class="hljs-attr">"gulp"</span>: <span class="hljs-string">"4.0.2"</span>,
        <span class="hljs-attr">"gulp-autoprefixer"</span>: <span class="hljs-string">"^8.0.0"</span>,
        <span class="hljs-attr">"gulp-concat"</span>: <span class="hljs-string">"^2.6.1"</span>,
        <span class="hljs-attr">"gulp-cssnano"</span>: <span class="hljs-string">"^2.1.3"</span>,
        <span class="hljs-attr">"gulp-livereload"</span>: <span class="hljs-string">"4.0.2"</span>,
        <span class="hljs-attr">"gulp-sourcemaps"</span>: <span class="hljs-string">"^3.0.0"</span>,
        <span class="hljs-attr">"gulp-uglify"</span>: <span class="hljs-string">"^3.0.2"</span>,
        <span class="hljs-attr">"postcss"</span>: <span class="hljs-string">"^8.4.20"</span>,
        <span class="hljs-attr">"tailwindcss"</span>: <span class="hljs-string">"^3.2.4"</span>
    }
}
</code></pre>
<p>You can learn more about <a target="_blank" href="https://ghost.org/docs/themes/content/">card_assets</a> and <a target="_blank" href="https://ghost.org/docs/themes/structure/#packagejson">config</a> for the theme. The config section helps add configuration for Ghost. You can also add more <a target="_blank" href="https://ghost.org/docs/themes/custom-settings/">custom configuration</a> for Ghost and enable and disable it with the Ghost UI. </p>
<p>To check all configurations, go to Ghost &gt; Settings &gt; Design &gt; and click Site-wide. There you can check all configuration lists provided by the theme developer.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/12/customconfig.png" alt="custom config enable and disable in ghost cms" width="600" height="400" loading="lazy">
<em>custom config enable and disable in ghost cms</em></p>
<h3 id="heading-how-to-use-theme-helpers">How to Use Theme Helpers</h3>
<p>The Ghost team provide lots of helpful functions to add additional functionality to the Ghost theme with <a target="_blank" href="https://handlebarsjs.com/">handlebars</a>. Some of the functionality by default comes with handlebars and other functionality is built by the Ghost team and maintained by the community.  </p>
<p>The Ghost team uses handlebars to build the entire Ghost CMS and theme. Basically, handlebars.js is a template language that helps you build both static and dynamic websites. </p>
<p>There are lots of Ghost helpers like <a target="_blank" href="https://ghost.org/docs/themes/helpers/foreach/">foreach</a>, <a target="_blank" href="https://ghost.org/docs/themes/helpers/get/">get</a>, <a target="_blank" href="https://ghost.org/docs/themes/helpers/if/">if</a>, <a target="_blank" href="https://ghost.org/docs/themes/helpers/is/">is</a>, <a target="_blank" href="https://ghost.org/docs/themes/helpers/match/">match</a>, <a target="_blank" href="https://ghost.org/docs/themes/helpers/config/">@config</a>, <a target="_blank" href="https://ghost.org/docs/themes/helpers/comments/">comments</a>, <a target="_blank" href="https://ghost.org/docs/themes/helpers/navigation/">navigation</a>, <a target="_blank" href="https://ghost.org/docs/themes/helpers/post/">post</a>, <a target="_blank" href="https://ghost.org/docs/themes/helpers/total_members/">total_members</a>, <a target="_blank" href="https://ghost.org/docs/themes/helpers/total_paid_members/">total_paid_members</a>, <a target="_blank" href="https://ghost.org/docs/themes/helpers/block/">block</a>, <a target="_blank" href="https://ghost.org/docs/themes/helpers/asset/">asset</a>, <a target="_blank" href="https://ghost.org/docs/themes/helpers/ghost_head_foot/">ghost_head</a>, <a target="_blank" href="https://ghost.org/docs/themes/helpers/ghost_head_foot/">ghost_foot</a>, <a target="_blank" href="https://ghost.org/docs/themes/helpers/pagination/">pagination</a>, <a target="_blank" href="https://ghost.org/docs/themes/helpers/partials/">partials</a>, <a target="_blank" href="https://ghost.org/docs/themes/helpers/body_class/">body_class</a>, <a target="_blank" href="https://ghost.org/docs/themes/helpers/block/">block</a>, <a target="_blank" href="https://ghost.org/docs/themes/helpers/search/">search</a> and many more. </p>
<p>You can read about all of the <a target="_blank" href="https://ghost.org/docs/themes/helpers/">helpers on the official docs</a>. You can also copy-paste some of the code so you do not need to remember. </p>
<h3 id="heading-what-is-the-partials-folder">What is the Partials Folder?</h3>
<p>The partials folder is like a component folder where you define all components for your theme. Basically, components are reusable code that you can reuse as often as you need. In the theme structure, we call these partials. All the partials are created with handlebars.js.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/12/partials.png" alt="partials for ghost theme" width="600" height="400" loading="lazy">
<em>partials for ghost theme</em></p>
<p>I create more than 24 partials for my Ghost theme and you can easily reuse them across websites. You can use partials with the following syntax: <code>{{&gt; your-partials-file-name}}</code>.</p>
<h3 id="heading-how-to-create-a-default-page">How to Create a Default Page</h3>
<p>First, we need to built a <code>default.hbs</code> file. The <code>default.hbs</code> file helps us build a layout for the website. Here's the code:</p>
<pre><code class="lang-handlebars">&lt;!DOCTYPE html&gt;

&lt;html class="dark scroll-smooth overflow-x-hidden" lang="{{@site.locale}}"&gt;

&lt;head&gt;
    &lt;meta name="viewport" content="width=device-width, initial-scale=1.0"&gt;

    {{!-- link production ready css file  --}}
    &lt;link rel="stylesheet" href="{{asset 'build/css/build.css'}}" /&gt;
   {{!-- ghost header --}}
      {{ghost_head}}

&lt;/head&gt;


&lt;body class="{{body_class}} bg-white dark:bg-slate-800 dark:text-white antialiased scroll-smooth "&gt;

  {{!-- partials/header --}}
    {{&gt; header}}

    &lt;main class="mt-6 flex flex-col"&gt;

{{!-- Render other pages  --}}
        {{{body}}}

    &lt;/main&gt;
{{!-- partials/footer --}}
    {{&gt; footer}}
    {{&gt; banner}}

{{!-- ghost header --}}
    {{ghost_foot}}

    &lt;script src="{{asset 'build/js/main.js'}}"&gt;&lt;/script&gt;

&lt;/body&gt;

&lt;/html&gt;
</code></pre>
<p>Let's see what's going on here:</p>
<ol>
<li><code>{{meta_title}}</code> provides the title from the website.</li>
<li>The <code>@site</code> is a global variable and you can access a title with <code>{{@site.title}}</code>. </li>
<li>Include a Ghost <code>{{ghost_head}}</code> in the head tag.</li>
<li>Include a Ghost <code>{{ghost_foot}}</code> in the footer tag.</li>
<li>Inserted all other templates with the <code>{{{body}}}</code> tag in index.hbs, post.hbs, and so on.</li>
<li>All other templates get inserted in index.hbs, post.hbs, and so on.</li>
<li>Include dynamic CSS classes with <code>{{body_class}}</code> in the <code>&lt;body&gt;</code> tag</li>
<li>Add footer partials in the default <code>{{&gt; footer}}</code> file</li>
<li>Add header partials in default <code>{{&gt; header}}</code> file</li>
<li>Include assets from the <code>{{asset "build/tailwind.css"}}</code> folder.</li>
</ol>
<h3 id="heading-how-to-create-an-index-page">How to Create an Index Page</h3>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/12/index.png" alt="Create index.hbs in the ghost theme" width="600" height="400" loading="lazy">
<em>Create <code>index.hbs</code> in the Ghost theme</em></p>
<p>The index page is the main page of the website. You can create a similar index page with the following code:</p>
<pre><code class="lang-handlebars">{{!--  Add default.hbs layout file --}}
{{!&lt; default}} 

{{!-- loop all the posts and show on home page --}}

{{#foreach posts}} 

{{!--  check condition defined in config section in package.json and add adsense code after every third post --}}
    {{#has number="nth:3" }} 
        {{#match @custom.adsense_enable "Enable" }} 
            {{&gt; ads}}
        {{/match}}
    {{/has}}

    {{!-- partials/postCard.hbs --}}
    {{&gt; postCard }}

    {{/foreach}}

    {{!-- Add pagination --}}
    {{pagination}}

{{!--  check condition defined in config section in package.json and add adsense --}}
    {{#match @custom.adsense_enable "Enable"}}
        {{&gt; ads}}
    {{/match}}

{{!-- newsletter partials --}}
    {{&gt; newsletter}}
</code></pre>
<p>You can access all posts with a for each loop and pass them to the partials with the <code>{{&gt; postCard}}</code> template. The <code>@custom.adsense_enable</code> is a custom config written in the <code>package.json</code> file and used in the theme to check that the website owner has enabled AdSense on-site or not. The custom config enables you to go to Ghost &gt; Settings &gt; Design &gt; and to click on Site-wide and enable Adsense.</p>
<h3 id="heading-how-to-create-a-posts-page">How to Create a Posts Page</h3>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/12/post.png" alt="Create post.hbs in the ghost theme" width="600" height="400" loading="lazy">
<em>Create <code>post.hbs</code> in the Ghost theme</em></p>
<p>The posts page is where readers will read your articles on your site. You can create a posts page with the following code.</p>
<pre><code class="lang-handlebars">{{!&lt; default}}

{{#post}}


{{!-- Pass reading_time time to authors partials with block , learn more about about block https://ghost.org/docs/themes/helpers/block/ --}}
{{#contentFor "fastestReadingTime"}}

 &lt;p class="text-slate-600 dark:text-slate-400 text-xs xs:text-xs sm:text-xs md:text-sm lg:text-sm xl:text-sm 2xl:text-sm "&gt;
    {{reading_time}}
 &lt;/p&gt;

{{/contentFor}}

{{#match @custom.adsense_enable "Enable"}}

    {{&gt; ads}}

{{/match}}

&lt;article class="{{post_class}} w-6/6 p-2"&gt;

    &lt;!-- Heading section --&gt;

    &lt;div class="m-auto mb-4 w-6/6 xs:w-5/6 sm:w-5/6 md:w-5/6 lg:w-5/6 xl:w-5/6 2xl:w-5/6"&gt;
        &lt;h1 class="mt-8 text-3xl xs:text-4xl sm:text-5xl md:text-5xl xl:text-5xl 2xl:text-5xl"&gt;
             {{title}} 
        &lt;/h1&gt;
        &lt;p class="text-slate-600 dark:text-slate-500 mt-2 text-1xl"&gt;
            {{excerpt}}
        &lt;/p&gt;
    &lt;/div&gt;

    &lt;!--  Author card partials/authors --&gt;
    {{&gt; authors}}



    &lt;!-- article thumbnail with partials/authors --&gt;
    {{&gt; featureImage}}


    &lt;!-- article body --&gt;
    &lt;div class="prose-lg prose-neutral m-auto p-2 my-10 w-10/12"&gt;
        {{content}}
    &lt;/div&gt;




&lt;/article&gt;


    {{!-- partials/comment --}}
    {{&gt; comment}}


{{!-- Add adsense  --}}
{{#match @custom.adsense_enable "Enable"}}

    {{&gt; ads}}

{{/match}}

{{!-- Show related posts --}}
{{#get "posts" filter="authors:{{primary_author.slug}}+id:-{{id}}" limit="3" include="authors"}}

{{!-- if post is available then show it --}}
{{#if posts}}

&lt;h2 class="mt-10 m-auto text-left w-5/6 text-xl xs:text-1xl sm:text-3xl md:text-4xl xl:text-5xl 2xl:text-6xl"&gt;
    Read more
&lt;/h2&gt;

{{!-- loop all post --}}
{{#foreach posts}}
    {{&gt; postCard }}
{{/foreach}}

{{/if}}

{{/get}}

{{!-- Add adsense  --}}
{{#match @custom.adsense_enable "Enable"}}

    {{&gt; ads}}

{{/match}}

{{/post}}

{{!-- newsletter partials --}}
{{&gt; newsletter}}
</code></pre>
<p>The fastestReadingTime block is to pass the reading time to the author partials. The <code>@custom.adsense_enable</code> is a custom config written in the <code>package.json</code> file and used in the theme to check that the website owner has enabled AdSense on-site or not. The custom config enables you to go to Ghost &gt; Settings &gt; Design &gt; and to click to Site-wide and enable Adsense.</p>
<h3 id="heading-how-to-create-informational-pages">How to Create Informational Pages</h3>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/12/page.png" alt="Create page.hbs in the ghost theme" width="600" height="400" loading="lazy">
<em>Create <code>page.hbs</code> in the ghost theme</em></p>
<p>The <code>page.hbs</code> file helps you create informational pages for your website. For example, you can create an about, contact, privacy policy, or disclaimer page on your site. </p>
<pre><code class="lang-handlebars">{{!&lt; default}} 

{{#post}} 

    {{!--Pass reading_time time to authors partials with block , learn more about about block https://ghost.org/docs/themes/helpers/block/     --}}
    {{#contentFor "fastestReadingTime"}}

    &lt;p class="text-grey-600 text-xs xs:text-xs sm:text-xs md:text-sm lg:text-sm xl:text-sm 2xl:text-sm "&gt;
        {{reading_time}}
    &lt;/p&gt;
    {{/contentFor}}

{{!-- Add adsense base of if enable on theme  --}}
    {{#match @custom.adsense_enable "Disable"}}

        {{&gt; ads}}

    {{/match}}

    &lt;article class="{{post_class}}  w-6/6 p-2"&gt;

        &lt;!-- Heading section --&gt;
        &lt;div class=" m-auto mb-16 w-6/6 xs:w-5/6 sm:w-5/6 md:w-5/6 lg:w-5/6 xl:w-5/6 2xl:w-5/6"&gt;
            &lt;h1 class="text-gray-800 mt-8 text-3xl xs:text-4xl sm:text-4xl md:text-5xl xl:text-6xl 2xl:text-8xl"&gt;
                {{title}}
            &lt;/h1&gt;
            &lt;p class="text-gray-600 text-xl xs:text-xl sm:text-xl md:text-1xl xl:text-2xl 2xl:text-2xl"&gt;
                {{excerpt}}
            &lt;/p&gt;
        &lt;/div&gt;

        &lt;!--  partials/authors --&gt;
        {{&gt; authors}}


        {{!--  partials/featureImage  --}}
        {{&gt; featureImage}}



        &lt;!-- article body --&gt;
        &lt;div class=" prose-xl prose-neutral m-auto p-2 my-10 w-10/12"&gt;
            {{content}}
        &lt;/div&gt;



    &lt;/article&gt;


    {{!-- Add adsense  --}}
    {{#match @custom.adsense_enable "Disable"}}

        {{&gt; ads}}

    {{/match}}

    {{/post}}
</code></pre>
<p>The fastestReadingTime block is to pass the reading time to the author partials. The <code>@custom.adsense_enable</code> is a custom config written in the <code>package.json</code> file and used in the theme to check that the website owner has enabled AdSense on-site or not. The custom config enables you to go to Ghost &gt; Settings &gt; Design &gt; and to click to Site-wide and enable Adsense.</p>
<h3 id="heading-how-to-create-an-author-page">How to Create an Author Page</h3>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/12/author.png" alt="Create author.hbs in the ghost theme" width="600" height="400" loading="lazy">
<em>Create <code>author.hbs</code> in the ghost theme</em></p>
<p>Author pages let you describe the author. You can show the author's name, bio, and related articles.</p>
<pre><code class="lang-handlebar">{{!&lt; default}}

{{#author}}

{{!--  Author Section pass with block learn more about about block https://ghost.org/docs/themes/helpers/block/ --}}
   {{#contentFor "authorName"}}
      {{name}}
    {{/contentFor}}

&lt;div class="container mt-20 mb-16 flex flex-col justify-between mx-auto"&gt;

    &lt;div class="flex flex-col mt-6 w-6/6 xs:w-5/6 sm:w-5/6  md:w-3/6 lg:w-3/6 xl:w-3/6 2xl:w-3/6 xs:mt-6 sm:mt-6 md:mt-6 lg:mt-0 xl:mt-0 2xl:mt-0 "&gt;

        &lt;h1 class="text-3xl mt-5 xs:text-3xl sm:text-3xl md:text-4xl xl:text-5xl 2xl:text-6xl"&gt; {{name}} &lt;/h1&gt;

        {{#if bio}}
            &lt;p class="mt-0 xs:mt-0 sm:mt-0 md:mt-1 lg:mt-3 xl:mt-3 2xl:mt-3 text-md"&gt;
                {{bio}}
            &lt;/p&gt;
        {{/if}}

        &lt;ul class="flex flex-row my-3"&gt;

            &lt;li class="text-md"&gt;{{location}}&lt;/li&gt;

               {{#if facebook}}

                    &lt;li class="mx-3 text-sm flex items-center"&gt;
                        &lt;a target="_blank" href="https://facebook.com/{{facebook}}" &gt;

                        {{!-- Pass partials/Icons/facebook --}}
                            {{&gt; Icons/facebook}}

                        &lt;/a&gt;
                    &lt;/li&gt;

                {{/if}}
                {{#if twitter}} 
                    &lt;li class="mx-3 text-sm flex items-center"&gt;
                        &lt;a target="_blank" href="https://twitter.com/{{twitter}}" &gt;

                        {{!-- Pass partials/Icons/twitter --}}
                            {{&gt; Icons/twitter}}

                        &lt;/a&gt;
                    &lt;/li&gt;
                {{/if}}
                {{#if website}} 
                    &lt;li class="mx-3 text-sm flex items-center"&gt;
                        &lt;a target="_blank" href="{{website}}" &gt;

                            {{!-- Pass partials/Icons/website --}}
                            {{&gt; Icons/website}}

                        &lt;/a&gt;
                    &lt;/li&gt;
                {{/if}}
        &lt;/ul&gt;
    &lt;/div&gt;
&lt;/div&gt;


{{!--  get posts related to author base on author Id --}}
    {{#get "posts" limit="all" filter="authors:{{slug}}+id:-{{id}}" order="published_at desc"   }}

        {{#if posts}}
                {{#foreach posts}}
                    {{&gt; authorCard}}
                {{/foreach}}
        {{/if}}

    {{/get}}

{{/author}}
</code></pre>
<h3 id="heading-how-to-create-a-tags-page">How to Create a Tags Page</h3>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/12/tag.png" alt="Create tag.hbs in the ghost theme " width="600" height="400" loading="lazy">
<em>Create tag.hbs in the ghost theme</em></p>
<p>You can use the <code>tag.hbs</code> file to show articles related to the tag used.</p>
<pre><code>{{!&lt; <span class="hljs-keyword">default</span>}}


{{#tag}}

&lt;div <span class="hljs-class"><span class="hljs-keyword">class</span></span>=<span class="hljs-string">"container m-auto mt-32  mb-16  w-5/6 xs:w-5/6 sm:w-5/6 md:w-5/6 lg:w-5/6 xl:w-5/6 2xl:w-5/6"</span>&gt;

    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">h1</span> <span class="hljs-attr">class</span>=<span class="hljs-string">" text-gray-800 text-4xl xs:text-5xl sm:text-6xl md:text-7xl xl:text-8xl 2xl:text-9xl"</span>&gt;</span>{{name}}<span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span></span>

    {{#<span class="hljs-keyword">if</span> description}}
        &lt;p <span class="hljs-class"><span class="hljs-keyword">class</span></span>=<span class="hljs-string">" text-gray-600 text-xl xs:text-xl sm:text-xl md:text-1xl xl:text-2xl 2xl:text-2xl"</span>&gt;
            {{description}}       
        &lt;/p&gt;
    {{/<span class="hljs-keyword">if</span>}}


&lt;/div&gt;

{{!--  get posts related to tag base on  tag slug --}}

    {{#get <span class="hljs-string">"posts"</span> include=<span class="hljs-string">"authors,tags"</span> limit=<span class="hljs-string">"3"</span> filter=<span class="hljs-string">"tag:{{slug}}"</span> <span class="hljs-keyword">as</span> |related|}}

   {{!-- loop posts base on article --}}
        {{#foreach related}}

    {{!--  check condition define <span class="hljs-keyword">in</span> config section <span class="hljs-keyword">in</span> package.json and add adsense code after every third post --}}
            {{#has number=<span class="hljs-string">"nth:3"</span>  }}
                {{#match @custom.adsense_enable <span class="hljs-string">"Enable"</span>}}
                    {{&gt; ads}}
                {{/match}}
            {{/has}}

        {{!-- partials/postCard.hbs --}}
            {{&gt; postCard }}

        {{/foreach}}

    {{/get}}

{{/tag}}
</code></pre><h3 id="heading-how-to-create-an-error-page">How to Create an Error Page</h3>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/12/404error-1.png" alt="Create error.hbs in the ghost theme" width="600" height="400" loading="lazy">
<em>Create <code>error.hbs</code> in the ghost theme</em></p>
<p>You use the <code>error.hbs</code> file to show when any errors occur on the website. Error pages help your website not break in production. The most common error is a 404 (not found) error.</p>
<pre><code>{{!&lt; <span class="hljs-keyword">default</span>}} 
&lt;div <span class="hljs-class"><span class="hljs-keyword">class</span></span>=<span class="hljs-string">"flex flex-col m-auto p-10 w-5/6 xs:w-5/6 sm:w-5/6 md:w-5/6 lg:w-5/6 xl:w-5/6 2xl:w-5/6"</span>&gt;
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">h1</span> <span class="hljs-attr">style</span>=<span class="hljs-string">"font-size: 10.8rem;"</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"text-black text-center"</span>&gt;</span>
        {{statusCode}}
    <span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span></span>

    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">p</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"text-4xl -m-6 text-center"</span>&gt;</span>
        {{message}}
    <span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span></span>

    <span class="xml"><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-center w-32 m-auto my-20 p-3 bg-black text-white items-center rounded-full"</span>&gt;</span>
        Home
    <span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span></span>
&lt;/div&gt;
</code></pre><h3 id="heading-how-to-enable-comments">How to Enable Comments</h3>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/12/comment-1.png" alt="enable comment in the ghost theme" width="600" height="400" loading="lazy">
<em>enable comments in the ghost theme</em></p>
<p>Ghost 5 officially supports <a target="_blank" href="https://ghost.org/docs/themes/helpers/comments/">the commenting system</a> (it's built-in) and you can just enable comments on the theme by copying and pasting the code – you never need any configuration. Ghost itself handles all the configurations. Here's the code:</p>
<pre><code class="lang-handlebars">&lt;div class="m-auto my-8 w-10/12"&gt;
    &lt;p class="text-right text-xs xs:text-xs sm:text-xs md:text-sm lg:text-sm xl:text-sm 2xl:text-sm "&gt;
        Before comment read our &lt;a style='text-decoration: underline' href="https://officialrajdeepsingh.dev/terms-and-conditions/"&gt;term and condition &lt;/a&gt;
    &lt;/p&gt;
    &lt;div class="mt-5 mb-5 p-4"&gt;

   {{!--  Enable comment on theme --}}
       {{comments}}
    &lt;/div&gt;
&lt;/div&gt;
</code></pre>
<h3 id="heading-how-to-set-up-search">How to Set Up Search</h3>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/12/serach-bar.png" alt="enable the search bar in the ghost theme" width="600" height="400" loading="lazy">
<em>enable the search bar in the ghost theme</em></p>
<p>Ghost 5 comes with the <a target="_blank" href="https://ghost.org/docs/themes/helpers/search/">official support of search functionality</a>. You do not need any other configuration. Just paste the following code into your theme and the search functionally will start working on your site. </p>
<pre><code class="lang-handlebars">{{!-- partials/Icons/search --}}
&lt;button class="gh-search" data-ghost-search&gt;{{&gt; Icons/search}}&lt;/button&gt;
</code></pre>
<h2 id="heading-conclusion">Conclusion</h2>
<p>Building a theme with Ghost is a relatively straightforward process compared to WordPress. The Ghost team has created well-defined documentation that you can easily follow as a beginner with examples. </p>
<p>They also provide many ready-made components, like search functionality, amp page, comments, and so on.</p>
<p>You can create your Ghost theme by copy-pasting the code. For beginner developers, it might seem a bit complicated, but you'll get the hang of it with some time and work. </p>
<p>The Ghost team has created a well-defined folder structure for theme development. It is the easiest way to manage the theme development process. You can also use npm packages to enhance the development process and add more functionality to the theme. In my theme, I use tailwind CSS and the Gulp package to speed up the development process.</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ Lume SSG Handbook – How to Create a Static Blog with Lume ]]>
                </title>
                <description>
                    <![CDATA[ Lume is a new static site generator based on Deno. Deno is a JavaScript-based run-time environment that supports TypeScript.  Lume is not built around any specific language. It supports Markdown, Nunjucks, TypeScript, and JavaScript by default. Lume ... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-create-a-static-blog-with-lume/</link>
                <guid isPermaLink="false">66d038af5ea8b15c90716651</guid>
                
                    <category>
                        <![CDATA[ Deno ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Static Site Generators ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Rajdeep Singh ]]>
                </dc:creator>
                <pubDate>Fri, 18 Nov 2022 17:06:55 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2022/11/Create-a-Static-Blog-with-Lume.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Lume is a new static site generator based on Deno. Deno is a JavaScript-based run-time environment that supports TypeScript. </p>
<p>Lume is not built around any specific language. It supports Markdown, Nunjucks, TypeScript, and JavaScript by default. Lume also supports plugins. Some plugins come preinstalled by default. This is why Lume itself is template-language agnostic.</p>
<p>Before learning more about Lume, let's discuss Deno and consider some important Deno features.</p>
<h2 id="heading-what-is-deno">What is Deno?</h2>
<p>Deno is an alternative to Node.js built by <a target="_blank" href="https://en.wikipedia.org/wiki/Ryan_Dahl">Ryan Dahl</a> (who also developed Node). Deno is based on the Rust programming language, and the second main component in Deno is the JavaScript V8 engine for WebAssembly.</p>
<p>Deno has many cool features – it's fast, secure by default, is compatible with web assembly and has TypeScript support, has in-built development tools, and more. Deno also supports Node.js APIs so you can use all npm packaged with Deno.</p>
<p>In Deno, you do not need to create a configuration file to run a simple program. You simply deploy your website instantly with a second on-edge network. But my final favorite feature is the new <code>node_modules</code> folder in the workspace. Deno caches all the packages locally and uses them, which is very fast compared to Node.</p>
<p>You can check out the <a target="_blank" href="https://minimalist-blog.deno.dev/">demo blog website here,</a> and all the <a target="_blank" href="https://github.com/officialrajdeepsingh/Minimalist-blog">code is available on GitHub here</a>.</p>
<p>Now let's dive into the tutorial.</p>
<h2 id="heading-table-of-contents">Table of Contents</h2>
<ol>
<li><a class="post-section-overview" href="#heading-lume-markdown">Lume + Markdown</a></li>
<li><a class="post-section-overview" href="#heading-why-is-lume-special">Why is Lume special?</a></li>
<li><a class="post-section-overview" href="#heading-how-does-lume-compare-to-other-static-site-generators">How does Lume compare to other static site generators?</a></li>
<li><a class="post-section-overview" href="#heading-how-to-start-a-new-project-with-lume">How to start a new project with Lume</a></li>
<li><a class="post-section-overview" href="#heading-lume-folder-structure">Lume folder structure</a></li>
<li><a class="post-section-overview" href="#additional-folders">Additional folders</a></li>
<li><a class="post-section-overview" href="#heading-how-to-create-global-data">How to create global data</a></li>
<li><a class="post-section-overview" href="#heading-how-to-create-a-dynamic-page">How to create a dynamic page</a></li>
<li><a class="post-section-overview" href="#heading-how-to-create-a-home-and-pagination-page">How to create a Home and Pagination page</a></li>
<li><a class="post-section-overview" href="#heading-how-to-build-an-articles-page">How to build an articles page</a></li>
<li><a class="post-section-overview" href="#heading-how-to-generate-a-category-page">How to generate a category page</a></li>
<li><a class="post-section-overview" href="#heading-how-to-generate-a-tag-page">How to generate a tag page</a></li>
<li><a class="post-section-overview" href="#heading-how-to-enable-search-functionality">How to enable search functionality</a></li>
<li><a class="post-section-overview" href="#heading-how-to-install-page-find">How to install page find</a></li>
<li><a class="post-section-overview" href="#heading-lume-seo">Lume SEO</a></li>
<li><a class="post-section-overview" href="#heading-lume-sitemap">Lume Sitemap</a></li>
<li><a class="post-section-overview" href="#heading-lume-plugins">Lume plugins</a></li>
<li><a class="post-section-overview" href="#heading-how-to-enable-comments">How to enable comments</a></li>
<li><a class="post-section-overview" href="#heading-how-to-use-netlify-cms-with-lume">How to use Netlify CMS with Lume</a></li>
<li><a class="post-section-overview" href="#heading-how-to-deploy-your-blog-with-deno-deploy">How to deploly your blog with Deno Deploy</a></li>
<li><a class="post-section-overview" href="#github-pages">Github pages</a></li>
<li><a class="post-section-overview" href="#heading-conclusion">Conclusion</a></li>
</ol>
<h2 id="heading-lume-markdown">Lume + Markdown</h2>
<p>Lume is a new static site generator based on Deno created and maintained by <a target="_blank" href="https://github.com/oscarotero">Óscar Otero</a>. Lume uses <strong>markdown-it</strong> as the default markdown. You can use the <a target="_blank" href="https://lume.land/plugins/remark/">remark plugin</a> to change the default markdown.  </p>
<p>Markdown is a language that helps to write documents, readme files, and blogs on the internet. <a target="_blank" href="https://en.wikipedia.org/wiki/John_Gruber">John Gruber</a> created markdown in 2004.</p>
<p><strong>Markdown-it</strong> is similar to <a target="_blank" href="https://github.github.com/gfm/">GitHub-flavored Markdown</a> (GFM) markdown. GFM and <a target="_blank" href="https://github.com/markdown-it/markdown-it">markdown-it</a> both follow the exact <a target="_blank" href="https://commonmark.org/">markdown specifications</a>. </p>
<p>If you've worked with GitHub and written README files, that means you are likely familiar with GFM markdown. If you don't like the default (markdown-it) markdown, you can change the markdown with the remark plugin.</p>
<p>There are tons of static site generators. So why is Lume special? What does it provide compared to other static site generators? Let's find out.</p>
<h2 id="heading-why-is-lume-special">Why is Lume special?</h2>
<p>As you know, Lume is built on Deno, and Deno is a Node.js alternative—that is why Lume provides lots of features out of the box. </p>
<p>Lume works similarly to a GitHub readme file. If you're familiar with writing one of those (and using markdown), you do not need to learn anything else to write articles and documentation with Lume.</p>
<p>Here are some benefits of Lume:</p>
<ol>
<li>Lume supports multiple template engines like Markdown, <a target="_blank" href="https://lume.land/plugins/nunjucks/">Nunjucks</a>, <a target="_blank" href="https://lume.land/plugins/eta/">Eta</a>, <a target="_blank" href="https://lume.land/plugins/jsx/">JSX</a>, <a target="_blank" href="https://lume.land/plugins/liquid/">Liquid</a>, or <a target="_blank" href="https://lume.land/plugins/pug/">Pug</a>.</li>
<li>It supports multiple authors</li>
<li>It has code syntax highlighting</li>
<li>There's great SEO support</li>
<li>Lume supports multiple languages</li>
<li>It has Windi CSS support</li>
<li>There's pagination and component support</li>
<li>It supports minifying JavaScript, HTML, CSS, and SASS</li>
<li>It has relations support</li>
<li>There is the built-in search functionality</li>
<li>It supports Netlify CMS</li>
<li>It supports images and SVGs</li>
<li>There's Remark.js plugin support</li>
<li>You can deploy with Netlify, Vercel, GitLab Pages, and the GitHub page.</li>
</ol>
<h2 id="heading-how-does-lume-compare-to-other-static-site-generators">How Does Lume Compare to Other Static Site Generators?</h2>
<p>Lume is a new static site generator compared to others, but it comes with many configuration options, and you can do anything with it. You don't even need to use any third-party plugins. </p>
<p>With Lume processors and preprocessors, you can easily manipulate the HTML code with the JavaScript DOM API. Other static site generators support a few template engines, but Lume supports many template engines like JavaScript, JSX, Nunjucks, Eta, JSX, Liquid, and Pug.</p>
<p>Note that Lume can seem tough to get started with for beginners. But if you're following my article, just make sure to <a target="_blank" href="https://github.com/officialrajdeepsingh/Minimalist-blog">open the code</a> which will make things much clearer for you.</p>
<h2 id="heading-how-to-start-a-new-project-with-lume">How to Start a New Project with Lume</h2>
<p>You can set up a new project with the Lume CLI with this command:</p>
<pre><code class="lang-bash">deno run -Ar https://deno.land/x/lume/init.ts
</code></pre>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/11/lume-installation-low.gif" alt="Lume installation demo" width="600" height="400" loading="lazy">
<em>Lume installation demo</em></p>
<h4 id="heading-follow-these-steps">Follow these steps:</h4>
<ol>
<li>First, create an empty  <code>mkdir lume-deno</code> folder project.</li>
<li>Then run the lume <code>init.ts</code> command.</li>
<li>Select an available plugin from the list.</li>
</ol>
<p>And you should be up and running.</p>
<h2 id="heading-lume-folder-structure">Lume Folder Structure</h2>
<p>After the installation finished, we saw three files:</p>
<ol>
<li><code>_config file</code> is used to configure Lume.</li>
<li><code>deno.json</code> is a defined script or task for Deno.</li>
<li><code>import_map.json</code> is to help you import a Deno package for the internet.</li>
</ol>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/11/folder-struture-1.png" alt="lume default folder structure " width="600" height="400" loading="lazy">
<em>lume default folder structure</em></p>
<h3 id="heading-how-to-run-the-lume-server">How to run the Lume server</h3>
<p>To run a local development server, you'll use the <code>deno task lume --serve</code> command. To build a website, run the <code>deno task build</code> command.</p>
<p>If you face a 404 - not found error, you can create a <code>index.njk</code> file within the root folder.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/11/lume404-1.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>In the <code>index.njk</code> file, paste the following code.</p>
<pre><code class="lang-nunjucks">---
title: "hello"
---
hello world
</code></pre>
<p>And you'll see the following output:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/11/hello-world-lume-2.png" alt="Image" width="600" height="400" loading="lazy">
<em>Lume hello-world</em></p>
<h3 id="heading-additional-folders">Additional folders:</h3>
<ol>
<li><code>posts</code> folder is not a compulsory folder. It contains all your posts' markdown files.</li>
<li><code>pages</code> folder is not a compulsory folder. It has all your pages' markdown files.</li>
<li><code>author</code> folder is not a compulsory folder. It has all your author markdown files.</li>
<li><code>_components</code> folder is a <strong>compulsory</strong> folder. It has all your components.</li>
<li><code>_includes</code>  folder is a <strong>compulsory</strong> folder. It contains your layout and templates for your site.</li>
<li><code>images</code> folder is not a compulsory folder. It contains all your images.</li>
</ol>
<p>The posts, pages, authors, and images folders are optional. You can rename these folders according to your wishes. The <code>_components</code> and <code>_includes</code> folders are mandatory and you don't rename them.</p>
<p>The difference between components, layout, and template are as follows:</p>
<ul>
<li>The components are reusable code</li>
<li>The layout and template are not reusable like components.</li>
</ul>
<h2 id="heading-how-to-create-global-data">How to Create Global Data</h2>
<p>In Lume, you can create a data variable, which has access to the entire website by all template engines.</p>
<pre><code class="lang-javascript"><span class="hljs-comment">// Set a variable</span>
site.data(<span class="hljs-string">"post_per_page"</span>, <span class="hljs-number">10</span>);

<span class="hljs-comment">// Set a function</span>
site.data(<span class="hljs-string">"randomNumber"</span>, <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">return</span> <span class="hljs-built_in">Math</span>.random();
});
</code></pre>
<h4 id="heading-how-to-create-posts-pages-and-author-markdown-files">How to create posts, pages, and author markdown files</h4>
<p>You create posts, pages, and author folders in the root folder. Then, inside every folder, you write markdown files.</p>
<p>You can access all posts, pages, and authors by file name on the browser:</p>
<ol>
<li><code>localhost:3000/posts/your-title.html</code></li>
<li><code>localhost:3000/pages/your-pages.html</code></li>
<li><code>localhost:3000/author/your-author.html</code></li>
</ol>
<p>Suppose you need a demo post, pages, and author markdown for a project or template. Then, you can use <a target="_blank" href="https://github.com/officialrajdeepsingh/Demo-markdown-posts">demo-markdown posts</a> for your project. It is free and open source, and you can create your own template.</p>
<h3 id="heading-how-to-create-a-dynamic-page">How to create a dynamic page</h3>
<p>In Lume, <code>.tmpl.js</code> and <code>.tmpl.ts</code> extensions use JS and TS as <a target="_blank" href="https://lume.land/plugins/modules/">template engines</a>. You can use them with regular pages or dynamic pages to create categories, tags, pagination, and so on for your website.</p>
<h3 id="heading-how-to-create-a-home-and-pagination-page">How to create a home and pagination page</h3>
<p>The home page is based on pagination, and pagination is based on posts. Lume dynamically generates the pagination. </p>
<p>In Lume, I chose nunjucks and JavaScript to create my demo website. Nunjucks is the default template engine. You can easily change the default Nunjucks engine with another template engine with copy-paste code.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/11/Home-page-1.png" alt="Image" width="600" height="400" loading="lazy">
<em>home page</em></p>
<p>Lume provides a JavaScript template function that helps create dynamic web pages. If you create a home page for the site, you need to create an <code>index.tmpl.js</code> file in your root or src folder. Lume also supports an src folder to organize your project. In my demo project, I'm not using the <code>src</code> folder.</p>
<p>The  <code>*.tmpl.js</code> is an extension of a <a target="_blank" href="https://lume.land/plugins/modules/#creating-pages">JavaScript template</a> that helps create dynamic pages for websites. It comes pre-installed in Lume with the <a target="_blank" href="https://lume.land/plugins/modules/">modules plugin</a>.</p>
<p>For example, the following code creates pagination for your website. But the layout comes from the <code>_includes</code> folder.</p>
<pre><code class="lang-javascript"><span class="hljs-comment">// index.tmpl.js</span>

<span class="hljs-comment">// title for SEO</span>
<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> title = <span class="hljs-string">"Minimalist blog"</span>
<span class="hljs-comment">// description for SEO</span>
<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> description = <span class="hljs-string">"Minimalist blog theme is liteweight and work with lume."</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-params">{ search, paginate }</span>) </span>{

<span class="hljs-comment">//  Get all posts of type article.</span>
  <span class="hljs-keyword">const</span> posts = search.pages(<span class="hljs-string">"type=article"</span>, <span class="hljs-string">"date=desc"</span>);

  <span class="hljs-comment">// Configation for pagination</span>
  <span class="hljs-keyword">const</span> options = {
    <span class="hljs-comment">// Page 1 is the homepage, set "/" as url</span>
    <span class="hljs-attr">url</span>: <span class="hljs-function">(<span class="hljs-params">n</span>) =&gt;</span> n === <span class="hljs-number">1</span> ? <span class="hljs-string">"/"</span> : <span class="hljs-string">`/page/<span class="hljs-subst">${n}</span>/`</span>,
    <span class="hljs-comment">// par page posts</span>
    <span class="hljs-attr">size</span>: <span class="hljs-number">7</span>,
  };

  <span class="hljs-comment">// Yield the pages, but the index needs a different layout</span>
  <span class="hljs-keyword">for</span> (<span class="hljs-keyword">const</span> page <span class="hljs-keyword">of</span> paginate(posts, options)) {

    <span class="hljs-comment">//  if home page, use diffrent layout "/"</span>
    <span class="hljs-keyword">if</span> (page.pagination.page === <span class="hljs-number">1</span>) {
      page.menu = {<span class="hljs-attr">visible</span>: <span class="hljs-literal">true</span>, <span class="hljs-attr">order</span>: <span class="hljs-number">1</span>, <span class="hljs-attr">title</span>:<span class="hljs-string">"Home"</span> }
      page.title = <span class="hljs-string">"Home page"</span>

      <span class="hljs-comment">//  comes from _includes folder</span>

      page.layout = <span class="hljs-string">"layouts/home.njk"</span>;
    } <span class="hljs-keyword">else</span> {
      <span class="hljs-comment">// Render diffrent layout if it is not home page page "/page/2","/page/3",etc</span>
      page.title = <span class="hljs-string">"Pagination page"</span>

      page.layout = <span class="hljs-string">"layouts/home.njk"</span>;
    }

    <span class="hljs-keyword">yield</span> page;
  }

}
</code></pre>
<p>‌Lume has a <a target="_blank" href="https://lume.land/plugins/search/">search plugin</a> that helps you search pages. In my demo blog, I search all pages base on the type. </p>
<p>In my all posts folder, all posts are defined in <code>type=article</code>, the author is described in <code>type=author</code>, and pages are defined in <code>type=page</code> . The search plugin is pre-installed with Lume.</p>
<p>On <code>index.tmpl.js</code> file, you can get all pages that have the type "article" (<code>type=article</code> ) using the following code:  <code>const posts = search.pages("type=article", "date=desc");</code>. The <code>search.pages("type=article", "date=desc")</code> function only returns those that have <code>type=article</code> .</p>
<p>The  <code>layouts/base.njk</code> layout file contains an HTML base and includes a header and footer for the website.</p>
<pre><code class="lang-nuckjunks">&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;{{ title }}&lt;/title&gt;
    &lt;meta name="description" content="{{ description or site.description }}"&gt;
   &lt;/head&gt;
  &lt;body&gt;

    {% include "layouts/header.njk" %}

    &lt;main class="{{ bodyClass }}"&gt;
      {{ content | safe }}
    &lt;/main&gt;

    {% include "layouts/footer.njk" %}

  &lt;/body&gt;
&lt;/html&gt;
</code></pre>
<p>Inside the <code>{{ content | safe }}</code>, Lume renders other HTML, like cards, articles, home templates, Tag and category pages, and so on.</p>
<pre><code class="lang-javascript"><span class="hljs-comment">// rest code ...</span>
  <span class="hljs-keyword">for</span> (<span class="hljs-keyword">const</span> page <span class="hljs-keyword">of</span> paginate(posts, options)) {
  }
<span class="hljs-comment">// rest code ...</span>
</code></pre>
<p>I used the for loop on the <code>index.tmp.js</code> file that helps get all the posts and send them to the <code>layouts/home.njk</code> file and the <code>layouts/home.njk</code> file. You get all posts from the result, and then pass them to the <code>card.njk</code> template.</p>
<pre><code class="lang-nunjucks">---
layout: layouts/base.njk
---

{% for post in results %}
    {% include "templates/card.njk" %}
{% else %}
    &lt;h2&gt; Posts is empty &lt;/h2&gt;
{% endfor %}

{% include "templates/pagnation.njk" %}
</code></pre>
<p>‌The <code>templates/card.njk</code>  file runs for all blogs and generates HTML for each blog. Your card looks like this:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/11/card.njk-1.png" alt="Image" width="600" height="400" loading="lazy">
<em>card.njk</em></p>
<p>In <code>card.js</code> template, you can access it using <code>{{}}</code> curly brackets. Get the title using <code>{{post.data.title}}</code> and <code>{{post.data.description}}</code>.</p>
<p>In my demo blog, I'm getting only the first category to show inside the card. So I use a defined filter  <code>_config.ts</code> and use it with <code>|</code> symbols. Inside <code>card.njk</code> we get a zero index or first value in from categories with the following code: <code>{{ post.data.category | category }}</code>.</p>
<p>To get the author on <code>card.njk</code> I define the <a target="_blank" href="https://lume.land/plugins/relations/">relationship</a> between the article and the author type, which you can learn about from the docs.</p>
<pre><code class="lang-nuckjunks">&lt;div class="container px-6 py-10 mx-auto"&gt;

    &lt;div class="mt-8 lg:-mx-6 lg:flex lg:items-center"&gt;

        &lt;img class="object-cover border-none w-full lg:mx-6 lg:w-1/2 rounded-xl h-72 lg:h-96" src="{{ post.data.image }}" alt="{{ post.data.title }}"&gt;

        &lt;div class="mt-6 lg:w-1/2 lg:mt-0 lg:mx-6 "&gt;

            &lt;a class="text-sm text-blue-500 uppercase" href="/category/{{ post.data.category | category }}" &gt;
                {{ post.data.category | category }}
            &lt;/a&gt;

            &lt;a href="{{ post.data.url }}" class="block mt-4 text-2xl font-semibold text-gray-800 hover:text-gray-500 dark:text-white md:text-3xl"&gt;{{ post.data.title }}&lt;/a&gt;

            &lt;p class="mt-3 text-sm text-gray-500 dark:text-gray-300 md:text-sm"&gt;
                {{ post.data.description }}
            &lt;/p&gt;

            &lt;a href="{{  post.data.url }}" class="inline-block p-2 bg-blue-700 mt-4 text-white hover:bg-blue-500"&gt;Read more&lt;/a&gt;


            &lt;div class="flex items-center mt-6"&gt;

                {% if post.data.author.length &lt;= 2 %}

                    {% for author in post.data.author %}

                        &lt;img class="border-none object-cover object-center w-10 h-10 rounded-full" src="{{ author.image}}" alt="{{ author.author_name}}"&gt;

                        &lt;div class="mx-4"&gt;
                            &lt;a href="{{author.url}}" class="text-sm text-gray-700 dark:text-gray-200"&gt;
                                {{ author.author_name}}&lt;/a&gt;
                            &lt;p class="text-sm text-gray-500 dark:text-gray-400"&gt;
                                {{author.job}}
                            &lt;/p&gt;
                        &lt;/div&gt;
                    {% endfor %}
                {% else %}

                    &lt;img class="border-none object-cover object-center w-10 h-10 rounded-full" src="{{ post.data.author.image}}" alt="{{ post.data.author.name}}"&gt;

                    &lt;div class="mx-4"&gt;
                        &lt;a href="{{ post.data.author.url}}" class="text-sm text-gray-700 dark:text-gray-200"&gt;
                            {{ post.data.author.author_name}}&lt;/a&gt;
                        &lt;p class="text-sm text-gray-500 dark:text-gray-400"&gt;
                            {{post.data.author.job}}
                        &lt;/p&gt;
                    &lt;/div&gt;
                {% endif %}

            &lt;/div&gt;
        &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;
</code></pre>
<p>The <code>{{ title }}</code> and <code>{{description}}</code> both show the markdown file title and description. To show the category, I used a filter to show a single category on the article page and define the filter on <code>_config.ts</code> file. I also show single and multiple authors with For loop. Every card has its own <code>post.data.url</code> property, after the user clicks on the read more button user ago respected the article read page. To show the image, I used <code>{{ post.data.image }}</code> property. I also show single and multiple authors with For loop on <code>card.njk</code> file.</p>
<h2 id="heading-how-to-build-an-articles-page">How to Build an Articles Page</h2>
<p>I know the page containing the article content is one of the most important for a blog. It's where readers should spend most of their time rather than the website's home page.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/11/random-blog-title-lume-1.png" alt="Image" width="600" height="400" loading="lazy"></p>
<pre><code class="lang-markdown">---
category:
<span class="hljs-bullet">  -</span> Blog
date: 2022-03-20T13:09:24Z
description: Dolor excepteur ad ad fugiat Lorem consectetur velit excepteur duis qui.
image: /images/dice.jpg
tags:
<span class="hljs-bullet">  -</span> npm
<span class="hljs-bullet">  -</span> npm cli
<span class="hljs-bullet">  -</span> npm install command
title: Random blog Title for markdown.
draft: false
author<span class="hljs-emphasis">_id: 1
type: article
layout: templates/article.njk
---

Laboris consequat elit ad excepteur. Ipsum duis amet dolore voluptate dolore consequat ullamco incididunt ullamco. Dolore laborum cupidatat dolor ipsum reprehenderit excepteur cupidatat dolore.

## First
Cupidatat non amet irure esse quis aute qui enim. Est qui ullamco proident consequat aute reprehenderit eiusmod nisi. Laboris ullamco fugiat sint occaecat.

## Second 
Irure fugiat officia non esse esse irure eu sint commodo quis amet. Dolor culpa non amet elit adipisicing exercitation ex anim velit ipsum.

## conclusion
Culpa irure eiusmod labore ut proident sit enim laborum nulla voluptate eu. Id tempor velit cillum pariatur est laboris ipsum ad. Sint nostrud nostrud laboris Lorem consequat tempor voluptate dolore velit. Commodo elit nulla commodo pariatur. Deserunt ipsum fugiat id ipsum pariatur cupidatat magna ex. Fugiat aliquip nisi laboris aliquip velit velit id quis eu reprehenderit excepteur fugiat.</span>
</code></pre>
<p>I created an article in the posts folder under <code>type=article</code> . The <code>author_id</code> defines the relation between the author and the article.</p>
<p>I used <code>templates/article.njk</code> as the layout for my articles page. You can design yours as per your requirements. You can design the article title, description, author card, and tags as well. </p>
<pre><code class="lang-nuckjunks">---
layout: layouts/base.njk
---
&lt;article class="container mx-auto p-2"&gt;
  &lt;div class="flex flex-col"&gt;

    &lt;h1 class="text-2xl text-black mt-3"&gt;{{ title }}&lt;/h1&gt;
    &lt;p class="text-xl mt-1 text-gray-600"&gt;{{ description }}&lt;/p&gt;

    {% if author %}
      &lt;div class="flex flex-row mt-4"&gt;


        {% if author.length &lt;= 2 %}

          {% for author in author %}

            &lt;img class="border-none object-cover object-center w-10 h-10 rounded-full" src="{{ author.image}}" alt="{{ author.author_name}}"&gt;

            &lt;div class="mx-4"&gt;
              &lt;a href="{{author.url}}" class="text-sm text-gray-700 dark:text-gray-200"&gt;
                {{ author.author_name}}&lt;/a&gt;
              &lt;p class="text-sm text-gray-500 dark:text-gray-400"&gt;
                {{author.job}}
              &lt;/p&gt;
            &lt;/div&gt;
          {% endfor %}

        {% else %}

          &lt;img class="border-none object-cover object-center w-10 h-10 rounded-full" src="{{ author.image}}" alt="{{ author.name}}"&gt;

          &lt;div class="mx-4"&gt;
            &lt;a href="{{ author.url}}" class="text-sm text-gray-700 dark:text-gray-200"&gt;
              {{ author.author_name}}&lt;/a&gt;
            &lt;p class="text-sm text-gray-500 dark:text-gray-400"&gt;
              {{ author.job}}
            &lt;/p&gt;
          &lt;/div&gt;
        {% endif %}

      &lt;/div&gt;

    {% endif %}

      &lt;nav class="flex flex-row my-5"&gt;
        {% for tag in tags %}
          &lt;a href="/tag/{{ tag.trim().toLowerCase().split(' ').join("-") }}/" class=" bg-blue-500 text-black p-2  mx-1"&gt;{{ tag }}&lt;/a&gt;
        {% endfor %}
      &lt;/nav&gt;

    &lt;time class="mt-2" datetime="{{ date | date('DATETIME') }}"&gt;
      {{ date | date('HUMAN_DATE') }}
    &lt;/time&gt;


  &lt;/div&gt;

  &lt;div class="mt-4"&gt;
    {{ content | safe }}
  &lt;/div&gt;
&lt;/article&gt;

{%- set previousPost = search.previousPage(url, "type=article") %}

{% if previousPost %}
  &lt;ul class="flex flex-row w-full mt-10 justify-between p-4"&gt;
    {%- if previousPost %}
      &lt;li class="w-6/12 text-left"&gt;
      ← Previous: &lt;a href="{{ previousPost.data.url }}" rel="prev"&gt;{{ previousPost.data.title }}&lt;/a&gt;
      &lt;/li&gt;
    {% endif %}

    {%- set nextPost = search.nextPage(url, "type=article") %}
    {%- if nextPost %}
      &lt;li class="w-6/12 text-right"&gt;
        &lt;strong&gt;Next: &lt;a href="{{ nextPost.data.url }}" rel="next"&gt;{{ nextPost.data.title }}&lt;/a&gt; →&lt;/strong&gt;
      &lt;/li&gt;
    {% endif %}
  &lt;/ul&gt;
{% endif %}

&lt;div class="container p-2 mx-auto mt-6"&gt; 

{# ==== #}
{#  Addding the utteranc Commenting script #}
{# ==== #}

&lt;h1 class="text-center text-2xl my-3"&gt; Comment &lt;/h1&gt; 

&lt;script src="https://utteranc.es/client.js"
        repo="officialrajdeepsingh/Minimalist-blog"
        issue-term="pathname"
        theme="github-light"
        crossorigin="anonymous"
        async&gt;
&lt;/script&gt;
&lt;/div&gt;
</code></pre>
<p>The <code>layouts/base.njk</code> file is the base file for our blog (which I've already explained). The <code>{{ title }}</code> and <code>{{description}}</code> both show the markdown file title and description. </p>
<p>To show tags on the article page, I used a for loop. I also showed single and multiple authors with the for loop. </p>
<p>To convert the date into a human-readable format, I used Lume date plugin and wrapped it with a date filter that looks like this: <code>{{ date | date('HUMAN_DATE') }}</code>. To show all markdown paragraphs, I used <code>{{ content | safe }}</code> . </p>
<p>For pagination, I used the Lume pagination plugin, and with the <code>search.previousPage(url, "type=article")</code> function, I showed the next and previous posts on the article page. For comments, I used <a class="post-section-overview" href="#heading-how-to-enable-comments">utteranc.es</a>.</p>
<h2 id="heading-how-to-generate-a-category-page">How to Generate a Category Page</h2>
<p>In Lume, you create a dynamic category based on article type. Lume also provides inbuilt functionality called a JavaScript template engine that helps you create a  dynamic page. It is similar to creating pagination functionality.</p>
<p>In Lume, there's a special file called <code>.tmpl.js</code> that helps you create a dynamic category.</p>
<pre><code class="lang-nunjucks">export const layout = "layouts/category.njk";

export default function* (props) {


  const { search }= props

  for (const category of search.values("category") ) {

    yield {
      url: `/category/${category}/`,
      title: `Categoryed ${category}`,
      type:"category",
      category,
    };

  }

}
</code></pre>
<p>In lume <code>search.values()</code> have a function that helps you find a category using markdown meta tags and sends data into the <code>layout/category.njk</code> file. It will generate all categories with the following URLs like  <code>/category/android/</code> , <code>/category/android-phone/</code> , <code>/category/human/</code> and so on.</p>
<h2 id="heading-how-to-generate-a-tag-page">How to Generate a Tag Page</h2>
<p>Generating a dynamic tags page is similar to a category. Lume provides a special <code>search.tags()</code> function to generate tags:</p>
<pre><code class="lang-nunjucks">export const layout = "layouts/tag.njk";

export default function* ({ search }) {

  for (const tag of search.tags()) {
    yield {
      url: `/tag/${tag}/`,
      title: `Tagged ${tag}`,
      type: "tag",
      tag,
    };
  }
}
</code></pre>
<p>The following code generates all tags with the following URLs like <code>/tag/android/</code>, <code>/tag/android-phone/</code>, <code>/tag/human/</code> and so on.</p>
<h2 id="heading-how-to-enable-search-functionality">How to Enable Search Functionality</h2>
<p>Lume has many in-built plugins which provide an excellent development experience. You can solve lots of problems with Lume plugins, and they allow you to add and remove features easily.</p>
<p>Lume provides inbuilt search functionality for the site. You enable it with the lume page find plugin.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/11/lume-serachbar-1.png" alt="Add a search bar in lume" width="600" height="400" loading="lazy">
<em>Add a search bar in lume</em></p>
<h3 id="heading-how-to-install-page-find">How to Install Page Find</h3>
<p>The Lume page finds plugin provides you with a search bar. Simply copy the following code and paste it into the <code>_config.ts</code> file and restart your server.</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> pagefind <span class="hljs-keyword">from</span> <span class="hljs-string">"lume/plugins/pagefind.ts"</span>;
</code></pre>
<h4 id="heading-how-to-configure-the-page-find-plugin">How to configure the page find plugin</h4>
<p>You configure the plugin in the <code>_config.ts</code> fil. You can also change the default config.</p>
<pre><code><span class="hljs-comment">// rest of code ...</span>
<span class="hljs-keyword">import</span> lume <span class="hljs-keyword">from</span> <span class="hljs-string">"lume/mod.ts"</span>;
<span class="hljs-keyword">import</span> pagefind <span class="hljs-keyword">from</span> <span class="hljs-string">"lume/plugins/pagefind.ts"</span>;

<span class="hljs-keyword">const</span> site = lume();

<span class="hljs-comment">// config the pagefind plugin with default config</span>
site.use(pagefind());

 <span class="hljs-comment">// or </span>

<span class="hljs-comment">// change the default config in pagefind plugin</span>
site.use(pagefind({
  <span class="hljs-attr">ui</span>: {
    <span class="hljs-attr">containerId</span>: <span class="hljs-string">"search"</span>,
    <span class="hljs-attr">showImages</span>: <span class="hljs-literal">false</span>,
    <span class="hljs-attr">showEmptyFilters</span>: <span class="hljs-literal">true</span>,
    <span class="hljs-attr">resetStyles</span>: <span class="hljs-literal">true</span>,
  },
}));

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> site;
</code></pre><h2 id="heading-lume-seo">Lume SEO</h2>
<p>Lume has a plugin to help with SEO called metas. With the plugin, you can easily add various SEO-friendly configurations.</p>
<h3 id="heading-how-to-install-metas">How to install metas</h3>
<p>You install all plugins within the <code>config.ts</code> file. Copy the following code and paste it into the <code>config.ts</code> file, then restart the server.</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> metas <span class="hljs-keyword">from</span> <span class="hljs-string">"lume/plugins/metas.ts"</span>;
</code></pre>
<h4 id="heading-how-to-configure-metas">How to configure metas</h4>
<p>You can configure metas in various ways in the  <code>_config.ts</code> file. See the comments below:</p>
<pre><code><span class="hljs-keyword">import</span> lume <span class="hljs-keyword">from</span> <span class="hljs-string">"lume/mod.ts"</span>;

<span class="hljs-comment">// install metas plugin for SEO</span>
<span class="hljs-keyword">import</span> metas <span class="hljs-keyword">from</span> <span class="hljs-string">"lume/plugins/metas.ts"</span>;

<span class="hljs-keyword">const</span> site = lume();

<span class="hljs-comment">// config the metas plugin with default config</span>
site.use(metas());

or

<span class="hljs-comment">// add custom config </span>
site.use(metas({
  <span class="hljs-attr">defaultPageData</span>: {
    <span class="hljs-attr">title</span>: <span class="hljs-string">"title"</span>, <span class="hljs-comment">// Use the `title` value as fallback.</span>
  },
}));


<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> site;
</code></pre><h3 id="heading-how-to-use-the-metas-seo-plugin-in-lume">How to Use the Metas SEO Plugin in Lume</h3>
<p>To use the SEO metas plugin, you'll need to create a <code>_data.yml</code> file in the root of the project folder and paste the following code into it:</p>
<pre><code>metas:
  site: Minimalist blog
  <span class="hljs-attr">twitter</span>: <span class="hljs-string">"@Official_R_deep"</span>
  <span class="hljs-attr">icon</span>: <span class="hljs-regexp">/images/i</span>con.png
  <span class="hljs-attr">lang</span>: en
  <span class="hljs-attr">generator</span>: <span class="hljs-literal">true</span>

<span class="hljs-attr">mergedKeys</span>:
  metas: object
</code></pre><p>The following code helps you create all the various SEO tags for your website, and you can easily extend it with the <a target="_blank" href="https://lume.land/plugins/metas/">metas plugin</a> in Lume.</p>
<h3 id="heading-lume-sitemap">Lume Sitemap</h3>
<p>Lume has a plugin called <a target="_blank" href="https://lume.land/plugins/sitemap/">sitemap</a>. This plugin helps you create sitemaps for your blog. With Lume 13 you do not need to create a sitemap manually. </p>
<h4 id="heading-how-to-install-the-sitemap-plugin">How to install the sitemap plugin</h4>
<p>You install all plugins within the <code>config.ts</code> file. Copy the following code and paste it into the <code>config.ts</code> file, then restart the server.</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> sitemap <span class="hljs-keyword">from</span> <span class="hljs-string">"lume/plugins/sitemap.ts"</span>;
</code></pre>
<h4 id="heading-how-to-configure-the-sitemap-plugin">How to configure the sitemap plugin</h4>
<p>You can configure the sitemap plugin in various ways in the <code>_config.ts</code> file. See the comments below:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> lume <span class="hljs-keyword">from</span> <span class="hljs-string">"lume/mod.ts"</span>;
<span class="hljs-keyword">import</span> sitemap <span class="hljs-keyword">from</span> <span class="hljs-string">"lume/plugins/sitemap.ts"</span>;

<span class="hljs-keyword">const</span> site = lume();

site.use(sitemap());

<span class="hljs-comment">// or</span>

<span class="hljs-comment">// add custom config </span>
site.use(sitemap({
  <span class="hljs-attr">filename</span>: <span class="hljs-string">"my-sitemap.xml"</span>, <span class="hljs-comment">// to change the sitemap filename</span>
  <span class="hljs-attr">query</span>: <span class="hljs-string">"indexable=true"</span>, <span class="hljs-comment">// Select only pages with the indexable attribute as true</span>
  <span class="hljs-attr">sort</span>: <span class="hljs-string">"date=desc"</span>, <span class="hljs-comment">// To sort by data in ascendent order</span>
}));

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> site;
</code></pre>
<h3 id="heading-how-to-use-the-sitemap-plugin-in-lume">How to use the sitemap plugin in Lume</h3>
<p>You do not need any special file to use the site map plugin. Simply add the plugin after calling the plugin in <code>config.ts</code> and it'll start working on your site. This creates the <code>sitemap.xml</code> file and you can change the file name with a custom configuration in <code>_config.ts</code> file.</p>
<h3 id="heading-how-to-access-the-sitemap-on-the-website">How to access the sitemap on the website</h3>
<p>You can access the sitemap with the filename, for example by default in the localhost <code>[http://localhost:3000/sitemap.xml](http://localhost:3000/sitemap.xml)</code> and production <code>[http://my-domain-name/sitemap.xml](http://localhost:3000/sitemap.xml)</code> . </p>
<h2 id="heading-lume-plugins">Lume Plugins</h2>
<p>Lume comes with <a target="_blank" href="https://lume.land/plugins/?status=all">inbuilt plugins</a>, but you can easily add or remove features according to your requirements. You do not need all the stuff on your site – you can configure everything as you wish. </p>
<p>You can add more template engines, minify HTML, CSS, and JavaScript with plugins, enable code highlighting, date manipulation, image manipulation, SVG support, and more. </p>
<p>You can also easily create your own plugins with lume. <a target="_blank" href="https://lume.land/docs/advanced/plugins/">Lume also provides excellent documentation</a> where you can learn more.</p>
<h2 id="heading-how-to-enable-comments">How to Enable Comments</h2>
<p>To add comments on your Lume site, I think <a target="_blank" href="https://utteranc.es/">utteranc.es</a> is the best choice for all static site generators. utteranc.es is an open-source commenting system based on GitHub. It looks like this:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/11/enable-coments-on-minimalist-blog.deno-1.png" alt="Enable comment in lume" width="600" height="400" loading="lazy">
<em>Enable comment</em></p>
<p>If you want to enable comments on the site, the first step is to install an <a target="_blank" href="https://github.com/apps/utterances">utterances application</a> on GitHub. Then, copy and paste the following code into the article read file or where you show comments on the site. </p>
<pre><code class="lang-javascript">&lt;script src=<span class="hljs-string">"https://utteranc.es/client.js"</span>
        repo=<span class="hljs-string">"officialrajdeepsingh/Minimalist-blog"</span>
        issue-term=<span class="hljs-string">"pathname"</span>
        theme=<span class="hljs-string">"github-light"</span>
        crossorigin=<span class="hljs-string">"anonymous"</span>
        <span class="hljs-keyword">async</span>&gt;
&lt;/script&gt;
</code></pre>
<p>Next, you'll need to change the utterance comment script. The first change in the repo <code>repo="your-github-repo"</code> name is compulsory. The others are not. You can adjust according to your requirements – for example, changing the theme, issue term, and so on. </p>
<p>To read more about utterance, here's a <a target="_blank" href="https://joshcollinsworth.com/blog/add-blog-comments-static-site">great article written by Josh Collinsworth</a>.</p>
<p>The best approach is to add utterance comments in lume and then read the <a target="_blank" href="https://github.com/lumeland/lume/discussions/312">GitHub discussion</a>.</p>
<h2 id="heading-how-to-use-netlify-cms-with-lume">How to Use Netlify CMS with Lume</h2>
<p>Netlify CMS is an open-source content management system. You can easily integrate Netlify with Lume using the <a target="_blank" href="https://lume.land/plugins/netlify_cms/">netllify_cms</a> plugin. It is provided by Lume, and you just need to install it and copy/paste the code.</p>
<h3 id="heading-how-to-install-the-netlify-plugin">How to Install the Netlify Plugin</h3>
<p>Import the Netlify plugin in your <code>_config.ts</code> file to use it like this:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> lume <span class="hljs-keyword">from</span> <span class="hljs-string">"lume/mod.ts"</span>;
<span class="hljs-keyword">import</span> netlifyCMS <span class="hljs-keyword">from</span> <span class="hljs-string">"lume/plugins/netlify_cms.ts"</span>;

<span class="hljs-keyword">const</span> site = lume();

site.use(netlifyCMS());

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> site;
</code></pre>
<p>To configure it, you'll need to create a <code>/_data/netlify_cms.yml</code> file in the root level and then paste the following code after restarting your server:</p>
<pre><code class="lang-yml"><span class="hljs-attr">backend:</span>
  <span class="hljs-attr">name:</span> <span class="hljs-string">git-gateway</span>
  <span class="hljs-attr">branch:</span> <span class="hljs-string">master</span>

<span class="hljs-attr">media_folder:</span> <span class="hljs-string">statics</span>

<span class="hljs-attr">collections:</span>
  <span class="hljs-bullet">-</span> <span class="hljs-attr">label:</span> <span class="hljs-string">Posts</span>
    <span class="hljs-attr">name:</span> <span class="hljs-string">posts</span>
    <span class="hljs-attr">description:</span> <span class="hljs-string">List</span> <span class="hljs-string">of</span> <span class="hljs-string">posts</span>
    <span class="hljs-attr">folder:</span> <span class="hljs-string">posts</span>
    <span class="hljs-attr">extension:</span> <span class="hljs-string">md</span>
    <span class="hljs-attr">create:</span> <span class="hljs-literal">true</span>
    <span class="hljs-attr">fields:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-attr">label:</span> <span class="hljs-string">Title</span>
        <span class="hljs-attr">name:</span> <span class="hljs-string">title</span>
        <span class="hljs-attr">widget:</span> <span class="hljs-string">string</span>
      <span class="hljs-bullet">-</span> <span class="hljs-attr">label:</span> <span class="hljs-string">Content</span>
        <span class="hljs-attr">name:</span> <span class="hljs-string">body</span>
        <span class="hljs-attr">widget:</span> <span class="hljs-string">markdown</span>
</code></pre>
<p>Netlify will ask you for permissions for the CMS proxy. Type <code>npx netlify-cms-proxy-server</code>  in a terminal, press enter or <code>type y</code>, and your Netlify CMS will start running locally on <a target="_blank" href="http://localhost:3000/admin">http://localhost:3000/admin</a> URL. Now your Lume blog is ready for deployment on Netlify. </p>
<h2 id="heading-how-to-deploy-your-blog-with-deno-deploy">How to Deploy Your Blog with Deno Deploy</h2>
<p>You can deploy Lume on various platforms such as Deno Deploy, GitHub Pages, Gitlab Pages, Netlify, Vercel, Fleek, AWS Amplify, and Cloudflare Pages. Lume also provides <a target="_blank" href="https://lume.land/docs/advanced/deployment/">excellent documentation on deployment</a>. </p>
<p>In this article, I'm deploying my Lume blog with Deno Deploy (and we'll also see how to do it with GitHub pages). Deno Deploy is an official platform built by the Deno team to deploy Deno-based applications.</p>
<p>Before deploying your Lume blog on Deno Deploy, make sure you create a <code>server.ts</code> file in the root level.</p>
<pre><code class="lang-javascript">
<span class="hljs-keyword">import</span> Server <span class="hljs-keyword">from</span> <span class="hljs-string">"lume/core/server.ts"</span>;

<span class="hljs-keyword">const</span> server = <span class="hljs-keyword">new</span> Server({
  <span class="hljs-attr">port</span>: <span class="hljs-number">8000</span>,
  <span class="hljs-attr">root</span>: <span class="hljs-string">`<span class="hljs-subst">${Deno.cwd()}</span>/_site`</span>,
});

server.start();

<span class="hljs-built_in">console</span>.log(<span class="hljs-string">"Listening on http://localhost:8000"</span>);
</code></pre>
<h4 id="heading-deployment-steps">Deployment Steps:</h4>
<ol>
<li>Create an account on Deno Deploy.</li>
<li>Push your local code to GitHub and then select the <code>server.ts</code> file. Deno Deploy automatically creates a site based on the <code>server.ts</code> the file.</li>
<li>Make sure to first create a custom <code>server.ts</code> file. Then move to the next step.</li>
<li>The easiest way to deploy your site is with GitHub Actions. Create a new <code>.github/workflows/deno.yml</code> file in your project root level and paste the following code into it:</li>
</ol>
<pre><code class="lang-yml"><span class="hljs-attr">name:</span> <span class="hljs-string">Deploy</span>
<span class="hljs-attr">on:</span> [<span class="hljs-string">push</span>]

<span class="hljs-attr">jobs:</span>
  <span class="hljs-attr">deploy:</span>
    <span class="hljs-attr">name:</span> <span class="hljs-string">Deploy</span>
    <span class="hljs-attr">runs-on:</span> <span class="hljs-string">ubuntu-latest</span>
    <span class="hljs-attr">permissions:</span>
      <span class="hljs-attr">id-token:</span> <span class="hljs-string">write</span> <span class="hljs-comment"># Needed for auth with Deno Deploy</span>
      <span class="hljs-attr">contents:</span> <span class="hljs-string">read</span> <span class="hljs-comment"># Needed to clone the repository</span>

    <span class="hljs-attr">steps:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Clone</span> <span class="hljs-string">repository</span>
        <span class="hljs-attr">uses:</span> <span class="hljs-string">actions/checkout@v3</span>

      <span class="hljs-comment"># <span class="hljs-doctag">TODO:</span> add a build step here</span>

      <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Upload</span> <span class="hljs-string">to</span> <span class="hljs-string">Deno</span> <span class="hljs-string">Deploy</span>
        <span class="hljs-attr">uses:</span> <span class="hljs-string">denoland/deployctl@v1</span>
        <span class="hljs-attr">with:</span>
          <span class="hljs-attr">project:</span> <span class="hljs-string">"minimalist-blog"</span>
          <span class="hljs-attr">entrypoint:</span> <span class="hljs-string">"./serve.ts"</span>
</code></pre>
<h2 id="heading-how-to-deploy-your-blog-with-github-pages">How to Deploy Your Blog with Github Pages</h2>
<p>GitHub Pages are free static sites you can use to host pages. You can also deploy your Lume blog it. The process of deployment is pretty easy. </p>
<p>To deploy Lume on GitHub pages you need to have GitHub Actions set up. </p>
<h4 id="heading-deployment-steps-1">Deployment Steps</h4>
<ol>
<li>It's best if you have a GitHub repository so you can convert your local website to GitHub Pages.</li>
<li>Create a new repo and push all your local code into it.</li>
<li>Create a new <code>.github/workflows/deno.yml</code> in your project root level, then paste the following code into it and push it into the GitHub repo. The GitHub action runs based on the <code>github.yml</code> action and it generates a GitHub page. </li>
</ol>
<pre><code class="lang-yml"><span class="hljs-attr">name:</span> <span class="hljs-string">Publish</span> <span class="hljs-string">on</span> <span class="hljs-string">GitHub</span> <span class="hljs-string">Pages</span>

<span class="hljs-attr">on:</span>
  <span class="hljs-attr">push:</span>
    <span class="hljs-attr">branches:</span> [ <span class="hljs-string">main</span> ]

<span class="hljs-attr">jobs:</span>
  <span class="hljs-attr">build:</span>
    <span class="hljs-attr">runs-on:</span> <span class="hljs-string">ubuntu-latest</span>

    <span class="hljs-attr">steps:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Clone</span> <span class="hljs-string">repository</span>
        <span class="hljs-attr">uses:</span> <span class="hljs-string">actions/checkout@v3</span>

      <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Setup</span> <span class="hljs-string">Deno</span> <span class="hljs-string">environment</span>
        <span class="hljs-attr">uses:</span> <span class="hljs-string">denoland/setup-deno@v1</span>
        <span class="hljs-attr">with:</span>
          <span class="hljs-attr">deno-version:</span> <span class="hljs-string">v1.x</span>

      <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Build</span> <span class="hljs-string">site</span>
        <span class="hljs-attr">run:</span> <span class="hljs-string">deno</span> <span class="hljs-string">task</span> <span class="hljs-string">build</span>

      <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Deploy</span>
        <span class="hljs-attr">uses:</span> <span class="hljs-string">crazy-max/ghaction-github-pages@v3</span>
        <span class="hljs-attr">with:</span>
          <span class="hljs-attr">build_dir:</span> <span class="hljs-string">_site</span>
        <span class="hljs-attr">env:</span>
          <span class="hljs-attr">GITHUB_TOKEN:</span> <span class="hljs-string">${{</span> <span class="hljs-string">secrets.MY_GITHUB_TOKEN_PAGE</span> <span class="hljs-string">}}</span>
</code></pre>
<p>You need a GitHub token to deploy your Lume website to GitHub pages. This is a required part of the setup. I found <a target="_blank" href="https://dev.to/github/the-githubtoken-in-github-actions-how-it-works-change-permissions-customizations-3cgp">a great article written by Davide</a> that can help you learn more about GitHub Actions and how to create one.</p>
<p>GitHub Actions takes two or three minutes to finish hosting your website on GitHub Pages. </p>
<p>Check out the <a target="_blank" href="https://github.com/officialrajdeepsingh/minimalist-blog-github-page">GitHub repository</a> to learn how to configure the GitHub workflow for GitHub pages. You can also see a live demo <a target="_blank" href="https://officialrajdeepsingh.github.io/minimalist-blog-github-page/">website on the GitHub page</a>.</p>
<p>A quick note: if you deploy your Lume site on GitHub pages and your image does not show on the website, there are two possible reasons for this:</p>
<ol>
<li>If all image names aren't in lowercase, you might get an error. To resolve the error, convert your image names into lowercase with this command: <code>your.github.com/your-reponame/images/my-image.png</code></li>
<li>If you're using the <code>base_path</code> and <code>relative_urls</code> Lume plugins in your project and <code>relative_urls</code> is redundant, and then you'll need to remove the <code>relative_urls</code> plugin in your project. Your image should now work fine.</li>
</ol>
<h2 id="heading-conclusion">Conclusion</h2>
<p>Lume is an easy-to-learn and feature-rich static site generator. You can do anything you imagine with it. Lume gives you a lot of freedom with the code.</p>
<p>The Lume community is not as big as those of Hugo, 11ty, Jekyll, and other tools. But, the Lume maintainers actively reply to everybody who comments in the GitHub discussion. Without a strong community, this tool should be able to create a strong impact.</p>
<p>One challenge with Lume is that it's tough to get started for beginners and is more suited to intermediate and advanced developers. If you're jumping right into using Lume as a beginner, you might struggle with a lack of background knowledge about how static site generators work. </p>
<p>Because of this, it's helpful to have a <strong>little bit of knowledge</strong> about Nuckjunks, JSX, and other template engines that work based on markdown. Once you gain this experience, then you'll easily be able to use Lume to design your markdown-based blog. </p>
<p>I recommend using the <a target="_blank" href="https://lume.land/plugins/mdx/">lume MDX plugin</a> for markdown. You can use JSX-based components inside the markdown file, and you can create beautiful code blocks, tip blocks, and so on.</p>
<p>I highly encourage all developers to try Lume out. If you have problems with Lume, you can reach out to its creator on the <a target="_blank" href="https://github.com/lumeland/lume/discussions">GitHub discussion</a> and the <a target="_blank" href="https://discord.com/invite/YbTmpACHWB">Discord server</a>. </p>
<p>If your course is about Computer science, Bioinformatics, and Biotechnology. You can join my free <a target="_blank" href="https://www.getrevue.co/profile/officialrajdeepsingh">newsletter</a>. </p>
 ]]>
                </content:encoded>
            </item>
        
    </channel>
</rss>
