Category: platforms

  • Jekyll vs. WordPress

    Jekyll vs. WordPress

    There are many choices when it comes to building a website or blog, catering to every level of technical knowledge. It can be overwhelming, especially because once you’ve chosen one, you probably won’t want to migrate to another one in the near future.

    This post compares two of the heavyweights: WordPress and Jekyll.

    Both are open source and very capable platforms, but with significant usability differences. WordPress is a content management system (CMS) that creates dynamic websites, with tens of thousands of plug-ins that expand the functionality of the site. Jekyll is a static site generator, so you build sites by generating static files (such as HTML) with your computer’s command line, and as such, there isn’t a database.

    While WordPress’s popularity isn’t likely to go away anytime soon, its position as an all-around system means there are instances where it offers more than you need. Like having a full toolbox when you just need a screwdriver. Jekyll, and other lightweight alternatives, offer a stripped-back experience that aims to provide only what you need, and no more.

    This piece will compare WordPress and Jekyll on the following:
    – Main features
    – Security
    – Speed
    – Stability
    – Flexibility
    – Design
    – Support

    Main Features

    The main difference between the two platforms is that WordPress is a CMS that builds dynamic sites, and Jekyll is a static-site generator reliant on command line-generated files.

    Both can create simple blogs or full websites, with navigation and multimedia. As it lacks dynamic features, Jekyll can’t provide tailored recommendations or user comments.

    Both platforms are also free and open-source, although there are various ways to spend money with WordPress. A self-hosted site will have hosting fees, which can vary widely, and you may choose to buy premium WordPress themes and plug-ins to get your site looking and working exactly as you want. Jekyll can be hosted for free on the GitHub servers.

    One major difference between WordPress and Jekyll is who they appeal to. Jekyll is likely to be more appealing to the person with some coding knowledge who is happy to spend time on the command line. WordPress, by contrast, has a graphical user interface and a database, and it’s beginner-friendly once you’re past the initial learning curve that any new platform has.

    Security

    You may have heard horror stories about how WordPress sites get hacked or targeted by cyberattacks, which can give the impression that WordPress itself isn’t secure.

    The good news is that this isn’t true. WordPress Core is run by a highly skillful team who release regular updates to fix any vulnerabilities and security threats. Just keeping your WordPress site updated will go a long way toward staying safe and secure.

    WordPress also allows you to install a wide range of themes and plug-ins, tweaking every aspect of design and functionality. Unfortunately, these are not all equal. As they’re created by third-parties, there’s variety in how stable and secure they are. It’s a good idea to stick to reputable names and the official WordPress directories.

    50 Ideas for your next blog post.

    To further increase peace of mind, you can add security plug-ins such as Wordfence that regularly scan for malware.

    The standard rules of online security come into play here: Use strong passwords, choose a secure hosting provider, and keep WordPress, plug-ins, and themes up to date.

    As a static site generator, Jekyll doesn’t need regular updates. Unlike WordPress, there’s simply nothing for a hacker to exploit. Because Jekyll’s files are stored locally, your main risk is if someone gains access to your computer.

    You can also store backups of the files in a cloud-based storage solution such as Dropbox, Google Drive, or iCloud. The static files take up trivial amounts of space, and if ever your site is compromised, you can simply download the files from the cloud and put them back on the server to regenerate your site.

    Speed

    While it’s not like comparing a Formula One car to a bicycle, this one isn’t exactly a fair fight. For all the speed optimizations WordPress offers, Jekyll loads static sites, which are fast.

    That’s not to say WordPress is slow, however. It can also be very fast, particularly with a good hosting provider and a well-chosen speed plug-in. The size of your site will also play a part; loading a simple, small blog is different from loading a huge landing page with lots of images, videos, and other resource-heavy elements.

    Nonetheless, there’s no getting away from the fact that WordPress is a dynamic site builder that loads from a database. Jekyll loads very small static files, and it can be sped up even further by using plug-ins such as ImageOptim API.

    Stability

    WordPress and Jekyll are both secure platforms. As already noted, WordPress Core is managed by a skillful and dedicated team, so new updates are regular and impactful.

    One area where WordPress’s stability can be called into question is when there’s a significant spike in traffic. On a dynamic site, the content has to be processed, and queries are sent to the database on the server. Higher traffic means more and more demands on the server, which can result in extremely slow rendering times or the site crashing entirely. This doesn’t damage the site, but it can be a frustrating user experience.

    Another point of consideration is that individual plug-ins can stop working properly on WordPress. This can sometimes affect the entire site, or it can cause specific components to stop working, such as widgets not loading correctly.

    Static-site generators like Jekyll don’t have the problem caused by traffic spikes, because each page is pre-rendered, and the per-visitor demands on the server are much lower.

    Flexibility

    It could be argued that if you looked up “flexibility” in the dictionary, there should be a picture of the WordPress logo next to it. Remember Apple TV commercials for the iPhone’s App Store, the ones that said “There’s an app for that”? Well, it’s a similar story with WordPress—whatever you want to do, there’s probably a plug-in for it, from Google Analytics to Facebook Pixel.

    You don’t necessarily have to rely on those plug-ins. If you’re comfortable with code, you can edit the CSS directly or add custom code without touching the main CSS file. Some companies, such as Thrive Themes, even let you create your own theme in a completely no-code environment. If that’s still more hands-on than you’d like, you can simply install one of the many, many thousands of free or premium themes available. And there are the very popular page builders, giving additional flexibility to individual pages without having to create your own theme. Elementor, Themify, and Thrive Themes are a few popular examples.

    This flexibility also means WordPress can be almost anything you want it to be. Simple blog? Yes. Landing page with opt-in form? Absolutely. Membership plug-in with gated content? Easy. Forum? Not a problem. E-commerce website for selling digital or physical goods? Of course.

    Jekyll is no slouch in the flexibility department either. Users with little or no coding experience will find it difficult, but experienced developers will be able to do almost anything through the code, from shifting the logo by a single pixel to nailing the overall design.

    As a static site generator, Jekyll has limitations compared to WordPress. It’s not possible to create a forum on Jekyll directly, for instance, and although you could include e-commerce solutions, they’re suitable only for smaller projects.

    Jekyll has a library of themes and plug-ins to choose from, but not nearly as many as WordPress offers.

    Design

    In many ways, this section will be largely influenced by your preference. Both WordPress and Jekyll sites can build beautiful sites in almost any design you want.

    Some people will find it an advantage that WordPress has a graphical user interface and CMS, while others will prefer Jekyll’s command line interface.

    Both display multimedia, although WordPress allows you to do some basic image editing on the platform, whereas Jekyll doesn’t. This might be a slight advantage for some.

    There are a lot of themes and plug-ins available for both platforms, too, giving you a quicker way of getting the design you want without building it from scratch. WordPress does have a far bigger library of both, though, so the design edge may have to go to the CMS here.

    Support

    Jekyll has multiple support options: official documentation, discussions on the Jekyll Forum and StackOverflow, as well as a Gitter channel and IRC channel on Freenode. Aside from the documentation, the support is dependent on the community, which is an active one.

    WordPress also has documentation, as well as official community forums. For sites hosted by WordPress, there is the option to talk to customer support on the phone. Because of its popularity, there are a lot of options for WordPress support, from hosting platforms to independent technical support companies.

    Conclusion

    WordPress and Jekyll are both mature and highly capable platforms. There are broad similarities between the two, but there’s enough of a difference in usability and features that picking the right one for you is likely to be a fairly easy decision to make.

    The Tech Content Manger's Playbook

    If you’re not experienced with code, want a CMS or graphical user interface, or want access to all of the themes and official plug-ins, WordPress is probably going to be your preference. You’ll need to stay on top of updates to keep the site secure, and you may also want to consider paying for monthly tech support to ensure everything—including plug-ins—stays up to date and to benefit from security and malware scans and from better hosting to prevent downtime from traffic spikes.

    On the other hand, if your ultimate preference is speed or security—and you’re comfortable with code and community support—Jekyll might be the way to go. The fact that it’s low-cost or free is persuasive, too.

  • Setting Up a Custom 404 Page with GitHub Pages

    Setting Up a Custom 404 Page with GitHub Pages

    In this article, I’ll be showing you how to create a custom error 404 page in GitHub Pages, a static site hosting service that allows you to publish and host your web pages through GitHub.

    To break that down quickly: a static site is a website made up entirely of HTML, CSS, and JavaScript files. Everything the site needs to render is contained within these files, so there’s no need for server technology like PHP, Node.js, or Python. If the static site needs extra data from a server, it makes an asynchronous HTTP request with tools like fetch api or axios. And you’ll probably want to make sure that static site has a 404 page.

    Displaying a 404 error page on your site is sort of like delivering bad news—the tone and manner of your delivery can determine if the person receiving the news will either be upset or be ready to agreeably look for another solution. After putting in all the hard work finding the best blogging platform, you don’t want a 404 to let you down.”Fortunately, GitHub Pages makes creating one a walk in the park.

    How Does GitHub Pages Work?

    Hosting your site on GitHub Pages is as easy as signing into GitHub and creating a new repository.

    Create a new GitHub repository

    Your repo name should have the following format: <your github username>.github.io.

    Name your GitHub.io repo

    Add files to your repo. You can do this either by creating the files locally and pushing to remote or by creating the files directly on GitHub.

    Go to the Settings tab of your newly created repository.

    Click Settings tab

    Scroll down to the GitHub Pages section.

    Here, you can choose the branch you want to serve your pages from, the root directory, and even select a Jekyll theme. If you already have a custom domain name, you can add it here.

    By adding a custom domain name, you can access your site using yourdomainname instead of your github.io domain.

    GitHub Pages settings

    That’s all! Your site is now ready to be published. Visit <your github username>.github.io to see your new website.

    You can make changes to your site whenever you choose. They become available immediately after you push/merge to the branch you’ve chosen to serve your pages from.

    Using GitHub Pages for your static site gives you some nice benefits:

    • GitHub Pages is free to use. No coughing up extra cash just to host some basic web pages.
    • You can use it to host websites like single-page applications or blogs.
    • You can add a custom domain name to your site.
    • You can enable HTTPS for your domain in just one click.
    • You get the added advantage of managing your site with Git. Version-controlling your website becomes a piece of cake.

    Why Is a 404 Page Important?

    The 404 page is a famously dreaded sight across the internet. It’s a page that neither website users nor owners like to see, but it’s an unavoidable one. Its purpose is to inform a user that the page they’re looking for doesn’t exist.

    Think about it: it isn’t every time you go into a store that you find everything you’re looking for. Certain items may be sold out or perhaps the store never sold them to begin with. That’s exactly what happens on the web. Perhaps the page’s URL changed, breaking an old link, or maybe the page was removed altogether. A user could have manually input the wrong URL to begin with. However a user has arrived at a particular URL, the requested web page doesn’t exist.

    Specifically, 404 is an HTTP status code for a “Resource Not Found” or simply “Not Found” error. It applies to all kinds of resources on the web, including HTML files, CSS files, JavaScript files, documents, images, and just about any other file that could be requested from a server.

    How you as the webmaster choose to convey a 404 error is very important. It can either improve your user’s experience on your site or make it worse.

    50 Ideas for your next blog post

    What Makes a Good 404 Page?

    At the end of the day, what’s on your 404 page is up to you, but I can give you a few tips to make it as palatable as possible for your users:

    • First and foremost, a 404 page should return with an actual 404 HTTP status code. This is important so web crawlers like Google’s bot don’t incorrectly index an erroneous page as a valid page, as this can negatively impact your SEO.
    • The 404 page should make it clear to the user that there’s a problem. This can be achieved by making the 404 or Not Found message prominent on the page.
    • The page should offer a way out to the user. Unless you want the user stuck on your error page or want them to close your site immediately, your 404 page should provide them with a means to navigate away from the page. Add an obvious link to your site’s homepage to somewhere on the error page, or you can suggest links to pages that might be similar to what the user was looking for in the first place.
    • You can provide a way for users to report a broken page, enabling you to quickly fix any issues.
    • Add a search bar for users to find other things they might be looking for.
    • Make the design pleasing to the eye. A 404 page is still a part of your website, so you should put as much thought into its design as you have for other valid pages. The page should carry elements of your brand, and not look like a desert island.

    GitHub’s very own 404 page is a good example of what a 404 page should look like. The error code and message are both prominent on the page, and there’s a search bar to help you find other content.

    GitHub’s error 404 page

    If you don’t add a custom 404 page to your GitHub Pages site, then by default GitHub displays the following 404 page:

    GitHub Pages default 404 page

    Not bad, eh? But not good enough for your brand.

    How to Create a Custom 404 Page in GitHub Pages

    Fortunately, GitHub also understands the importance of a 404 error page, and provides developers an easy way to add one to their GitHub-hosted pages. Let’s look at how you can add your own custom 404 page.

    Create a new file in the root of your repository and name it 404.html.

    Add file to repo

    Name 404 page

    Then go on to add content for the 404 page as follows:

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <title>Page Not Found</title>
        <link rel="preconnect" href="https://fonts.gstatic.com">
        <link href="https://fonts.googleapis.com/css2?family=Chango&family=Roboto:wght@300&display=swap" rel="stylesheet">
        <style>
            body{
                background-image: url("https://images.unsplash.com/photo-1616235132417-99f443954d2e?ixid=MXwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHw%3D&ixlib=rb-1.2.1&auto=format&fit=crop&w=334&q=80");
                background-size: cover;
                font-family: 'Roboto', 'sans-serif';
            }
            main{
                display: flex;
                height: 100vh;
                justify-content: center;
                align-items: center;
                flex-direction: column;
            }
            .header404{
                font-family: 'Chango', cursive;
                font-size: 48px;
            }
            .content404{
                text-align: center;
            }
        </style>
    </head>
    <body>
        <main>
            <h4 class="header404">404</h4>
            <div class="content-404">
                Uh-oh! We couldn't find the page you are looking for.
            </div>
            <p>
                <a href="/">Check our home page</a>
            </p>
        </main>
    
    </body>
    </html>
    

    Commit your changes, then try to visit a page that doesn’t exist on your site, eg,username.github.io/a-random-page-that-doesn't exist. A page like this should appear:

    Custom error 404 page design

    Conclusion

    Hopefully, this article has shed some light on the importance of a 404 page and why you should put some effort into branding it as you would other pages of your website. When properly done, a 404 error page can be a good way to increase your website’s conversion rate.

    Unlike many other host servers where you have to tinker with .htaccess or other kinds of configuration settings to handle 404 error pages, GitHub Pages makes it incredibly easy. All you need to do is add a single file, and you’re good to go.

    If you’re stuck for ideas on what your 404 error page can look like, you can always look around for some 404 inspiration.

  • Hugo vs. Jekyll: Which is Right for Your Blog?

    Static site generators create HTML sites, with predictable page layouts and content with regular presentation such as blogs. There are many frameworks that can leverage a programming language and allow you to reuse code and process assets for these HTML pages, but this article will compare two of the most popular: Hugo and Jekyll. (For a comparison of a static site and a dynamic site, see Jekyll vs WordPress)

    Why Use Static HTML Pages?

    Static site generators are appealing because they produce secure sites requiring little maintenance that are faster to serve than dynamically generated web pages. With dynamic web pages, a web framework installed on a server generates the page a user sees. A user makes a request, the server queries a database, pulls out the information users want to see, combines that data into an HTML page, then sends that page to a user.

    With static HTML pages, the pages are pre-rendered, so the server doesn’t do any of the work of building the page. It only handles sending the appropriate page to the user. This means that static HTML pages are faster, and the computing requirements for the server are much smaller; important in cloud environments where you’re charged for computing power.

    Static pages cut out the page generation, so the user receives their page much more quickly. Security risks are also smaller because there are fewer moving parts for attackers to infiltrate and exploit. Static HTML pages are easily cached, so they’re well suited to be served from content delivery networks (CDNs), making response times even faster.

    Because static pages don’t require servers to perform calculations or query the database, you can deploy them using very simple, low-cost hosting options like an Amazon S3 bucket or GitHub Pages.

    Static Site Generators

    You could simply write an HTML page and put it on a server, hearkening back to the early days of the web, but static site generators make it much easier to create new pages that use existing templates or modify all of your existing pages at once.

    One of the first static site generators to restart this trend in web development was Jekyll. Hugo joined in five years later.

    What is Hugo?

    Hugo is a super fast, highly secure static site generator that positions themselves as a fun and modern website building tool. It is written in Go and sites can be hosted anywhere. One of the more standout features is that Hugo static sites don’t need a database on runtimes like Ruby, PHP or Python. According to their website, Hugo static site generator “is the fastest tool of its kind” listing average site builds speeds of under a second. \
    More information on “What is Hugo”.

    What is Jekyll?

    Jekyll is an open source static site generator written in Ruby by the co-founder of Github, Tom Preston-Werner. It translates plain text documents into static sites that can be used for informational based websites or blogs. It has built-in support for Github pages and is one of the more popular static site generators available.\
    More information on “What is Jekyll

    Hugo vs. Jekyll

    With either generator, you can get a templated blog up and running in under thirty minutes. If you’re starting from nothing, Hugo is slightly easier to install. With Jekyll, you have to install a couple prerequisites like Ruby. Go comes as a precompiled binary bundled along with the Hugo installation.

    For both Jekyll and Hugo frameworks, you’d normally write a content file like a blog post in HTML or Markdown. This content gets combined with HTML templates, which wrap and style the content, outputting an HTML file for display on the web.

    Both frameworks allow developers to add variables to content, using the YAML markup language, and consume data files in common formats like JSON and CSV. Both frameworks also come with a number of features useful for a blog, like tags and the ability to route content files to finished HTML pages. They are also open source, so you can request changes and contribute improvements.

    The first and most fundamental difference between the Jekyll and Hugo  frameworks is the language they’re written in. Jekyll is written in Ruby, a popular scripting language that was one of the first languages to come with an opinionated web framework, making it extremely popular for building websites quickly. Hugo is written in Go, which was developed at Google with an eye on concurrent execution, optimizing for deployment in cloud environments where computing power is distributed across many machines.

    Each framework also has different preferences about what languages it works well with. Jekyll offers support for CoffeeScript and SASS/SCSS. Hugo supports TOML and JSON markdown in content files, but supporting SASS and SCSS might require some additional setup.

    The Jekyll framework

    The Template Situation

    One of the main benefits of the Jekyll framework is its ease of use, well-developed documentation, and broad support from major organizations like GitHub. Jekyll was released twelve years ago and helped kick off the new interest in static HTML sites. Hugo was released later and is less popular, so it has a less developed ecosystem of plug-ins and templates.

    GitHub topics offers a whopping 1,200 themes to choose from for Jekyll, while there are only 370 options offered on the Hugo themes pages (although, you can also create your own themes). Obviously, it’s much more likely you’ll find a theme with the look you want with Jekyll. Jekyll is supported by GitHub, so if you want a simple, no-cost deployment, Jekyll works seamlessly with GitHub Pages, so you can have a simple Jekyll blog up and online very quickly by following GitHub’s excellent documentation.

    50 Ideas for your next blog post.

    Another difference between the Hugo and Jekyll frameworks is that creating a brand-new site with Jekyll by running the command jekyll new my-awesome-site installs a basic theme, while creating a new site with the hugo new site my-awesome-site command only generates the folder structure and an archetype file. With Jekyll, you’ll have something to work with right away, but with Hugo you’ll be looking at an empty screen waiting for you to add a theme or custom templates.

    This can be great for a totally customized setup, but Jekyll has a much quicker path to seeing content you can work with.

    First post with Jekyll

    Speed, More Speed

    One of the major benefits of using the Hugo static site generator is its speed. Leveraging the focus on concurrency of the Go language means blogs with thousands of entries or tons of images will generate HTML more quickly. That matters if you’re running code anywhere you’re paying for computing power.

    It also matters during development, because changes that you make to templates or content are re-rendered more quickly with Hugo. This speed difference is noticeable even at low page counts, but it becomes significant if you’re building a hundred pages of content.

    The Hugo framework

    Hugo Bonuses

    Hugo offers support for internationalization, providing multiple ways to categorize content in different languages. Hugo also offers image processing, built-in menus, site mapping, and live reloading.

    You can achieve the same result in Jekyll, but it’ll take more work to set up. In Jekyll, this functionality comes from plug-ins, but if you’re building complicated pages, it’s nice to have it built-in.

    Hosting

    Both languages offer options for easy hosting, but Jekyll is the simplest. Jekyll and GitHub Pages have a close, long relationship, and deploying a Jekyll project to GitHub Pages is simple and fast, which can be a great option for trying out a blog with Jekyll.

    Hugo also offers many hosting options. For both generators, you have two fundamental options:

    1. You can run the site generator locally, then upload the results to a server. You can do this manually or instruct some service to grab your updated HTML.
    2. You can install the static site generator on a computer in the cloud, tell that computer to run the content generation command, then serve the files that process creates. This is how services like Amazon Amplify, CloudCannon, and Netlify all work. These providers all have specific guides for deploying sites, but the deployment process is pretty painless when using either static site generator.

    What to Choose

    Jekyll and Hugo are both well suited for blogs and other frontend-oriented sites. They generate static HTML pages by combining content written in a markdown language with HTML templates.

    The Jekyll static site generator has the fastest setup, more options for templates, and offers an easier experience when getting started, but it can start to feel slow once you’re processing a hundred pages. Hugo’s initial setup is more complicated, but it can handle larger sites with more speed, for example a Hugo blog.

    When deciding, think about the languages you’re familiar with, the types of markdown you want to use, and how you’ll deploy the site. If you want to start blogging right away, Jekyll is a great fit, but if you anticipate writing lots of content, the speed and features of Hugo will make your development experience smoother.

  • Creating Gatsby Starters

    Creating Gatsby Starters

    GatsbyJS is an open-source React-based, GraphQL powered static site generator. You can create blazingly fast websites in minutes using Gatsby, and many technical companies use it for their blogs.

    Gatsby Starters are boilerplates that you, as a developer, can use to set up a new site with preconfigured tools and plugins instantly. You can modify and build on top of the starters to speed up your development process.

    You might ask, why create your own starter? Why not just use an existing starter? There are a few reasons developers invest the time building a starter:

    • To help other developers build a better site on Gatsby using unique features or designs.
    • To showcase it in the official Gatsby Starter Library and gain recognition for your work.
    • To promote your freelance services by creating a portfolio starter with your information. Every time someone uses your starter, they will see your portfolio.

    Tutorial Overview

    In this tutorial, I’ll show you how to build a Gatsby starter from scratch using MDX and Styled Components, the best CSS-in-JS tool. If you want to jump right into the code, check out the GitHub Repo here, and check out the deployed version here.

    More specifically, here’s what I’ll cover in this tutorial:

    Prerequisites

    Before we get started, you should have:

    1. Knowledge of HTML, CSS, and JavaScript.
    2. Basic knowledge of React, Gatsby, and GraphQL.
    3. Node and NPM installed on your local dev machine.
    4. Any code editor of your choice.
    5. React Dev Tools (optional)

    Setting Up Gatsby

    {% raw %}
    In this tutorial, you will use the Gatsby CLI tool to generate a Gatsby-powered site with minimal configurations quickly.

    Run the following command in your terminal to create a Gatsby site with minimal configuration using Gatsby Hello World Starter.

    gatsby new gatsby-starter-blog https://github.com/gatsbyjs/gatsby-starter-hello-world
    cd gatsby-starter-blog
    gatsby develop
    

    You can read more about setting up your Gatsby development environment here.

    gatsby-source-filesystem Plugin

    The next step is to install the plugins you will use in this starter blog, the first of which is gatsby-source-filesystem. This plugin creates File nodes which point to the location of each file to the GraphQL query gets data from.

    You can read more about sourcing from the file system here.

    Run the following command in your project’s root directory.

    npm install gatsby-source-filesystem
    

    Now, update the gatsby-config.js file to use this plugin.

    // gatsby-config.js
    module.exports = {
      plugins: [
        {
        resolve: `gatsby-source-filesystem`,
        options: {
            name: `pages`,
            path: `${__dirname}/src/pages`,
        },
        },
        {
        resolve: `gatsby-source-filesystem`,
        options: {
            name: `posts`,
            path: `${__dirname}/src/posts`,
        },
        },
        {
        resolve: `gatsby-source-filesystem`,
        options: {
            name: `images`,
            path: `${__dirname}/src/images`,
        },
        },
      ],
    };
    

    In the above code, you have three instances of the gatsby-source-filesystem plugin for pages, posts, and images folder. All the JavaScript files like index.js reside in the pages directory, posts folder is where your articles are stored. The images folder is for any images or image resource you might need in your blog like your profile picture.

    50 Ideas for your next blog post

    Now, create the posts and images folders inside the src directory. Run the following commands in the terminal.

    cd src
    mkdir posts
    mkdir images
    cd ..
    

    gatsby-plugin-google-fonts Plugin

    I like using Google Fonts as a free improvement over most operating systems’ standard font options. To use Google Fonts in Gatsby, install the gatsby-plugin-google-fonts plugin.

    npm install gatsby-plugin-google-fonts
    

    Update gatsby-config.js to use the gatsby-plugin-google-fonts plugin.

    {
      resolve: `gatsby-plugin-google-fonts`,
      options: {
        fonts: ["Satisfy", "Montserrat", "JetBrains Mono"],
        display: "swap"
      }
    }
    

    Feel free to explore Google Fonts and import the fonts of your choice.

    gatsby-plugin-sharp and gatsby-transformer-sharp Plugins

    Optimizing images on your blog improves user experience by making response times faster. This in turn can help your blog’s ranking in search engines and makes your site more usable on mobile internet connections, so it’s important to optimize your images.

    Run the following command to install the gatsby-plugin-sharp and the gatsby-transformer-sharp plugins that will help optimize the images in your starter.

    npm install gatsby-plugin-sharp gatsby-transformer-sharp
    

    gatsby-plugin-mdx Plugin

    MDX is a variant of traditional markdown that lets you write JSX in your markdown files. With MDX you can style and customize your posts in the markdown file itself and can even load dynamic data in them.

    Run the following command to install the gatsby-plugin-mdx.

    npm install gatsby-plugin-mdx @mdx-js/mdx @mdx-js/react gatsby-remark-images
    

    This plugin will allow you to create pages with .mdx and .md extensions in src/posts and process any Gatsby nodes with Markdown media types into MDX content.

    Update gatsby-config.js to include the gatsby-plugin-mdx, gatsby-plugin-sharp and gatsby-transformer-sharp plugins.

    // gatsby-config.js
    module.exports = {
      plugins: [
        {
        resolve: `gatsby-source-filesystem`,
        options: {
            name: `pages`,
            path: `${__dirname}/src/pages`,
        },
        },
        {
        resolve: `gatsby-source-filesystem`,
        options: {
            name: `posts`,
            path: `${__dirname}/src/posts`,
        },
        },
        {
        resolve: `gatsby-source-filesystem`,
        options: {
            name: `images`,
            path: `${__dirname}/src/images`,
        },
        },
        {
        resolve: `gatsby-plugin-google-fonts`,
        options: {
            fonts: ["Satisfy", "Montserrat", "JetBrains Mono"],
            display: "swap",
        },
        },
        `gatsby-plugin-sharp`,
        `gatsby-transformer-sharp`,
        {
        resolve: `gatsby-plugin-mdx`,
        options: {
            extensions: [`.md`, `.mdx`],
            gatsbyRemarkPlugins: [
            {
                resolve: `gatsby-remark-images`,
                options: {
                maxWidth: 590,
                },
            },
            ],
        },
        },
      ],
    };
    

    Using Styled Components with Gatsby

    Styled Components is an amazing CSS-in-JS tool that lets you write CSS in your JavaScript files. This makes it easy to manage all your styling without having any CSS files in your project.

    There are many other benefits and advantages of CSS-in-JS like managing dead code elimination becomes easier with it, styles are scoped to a specific component. It also makes it easier to write unit tests. Many CSS-in-JS libraries also have built-in support for animations, source maps and caching.

    Run the following command in the terminal to install the gatsby-plugin-styled-components plugin.

    npm install gatsby-plugin-styled-components styled-components babel-plugin-styled-components
    

    Update the gatsby-config.js file to include this plugin.

    module.exports = {
      plugins: [
        // other plugins
    
        `gatsby-plugin-styled-components`,
      ],
    };
    

    Now create a global-styles.js file to add global styles and theme to the app. Global Styles as the name suggests are the styles that are scoped to the global level in your app. For example, if you define the font-size property as 3rem in global styles then the font size of all text will be changed to 3rem across your app.

    A theme is an object or set of styles that you can define and access in your CSS-in-JS. For example, if you want to add dark mode to your app, you can define two nested objects in the theme object, one for light mode and another for dark mode. Each object will have properties like background color, font color, etc., and when the user clicks the dark mode button, it will toggle between the light and dark styles object.

    You can read more about this here.

    Run the following command in your terminal.

    cd src
    mkdir themes
    cd themes
    touch global-styles.js
    

    Add the following code to the global-styles.js file.

    // global-styles.js
    import { createGlobalStyle } from "styled-components";
    
    export const theme = {
      colors: {
        primary: "#363537;",
        primaryDark: "#313032",
        primaryDarker: "#2e2d2f",
        primaryDarkest: "#262527",
        primaryLight: "#3b3a3d",
        primaryLighter: "#3e3d3f",
        primaryLightest: "#464548",
      },
      fonts: "Montserrat, Verdana,sans,  serif",
      fontSizes: {
        small: "1em",
        medium: "1.5em",
        large: "2em",
      },
    };
    
    export const GlobalStyles = createGlobalStyle`
    *, *:before, *:after {
        box-sizing: border-box;
        margin:0;
        padding:0;
      }
    
      html {
        scroll-behavior: smooth;
        font-family: ${(props) => props.theme.fonts};
        background-color:#f7f7f7;
        font-size: ${(props) => props.theme.fontSizes.medium};
        padding: 0 1rem;
      }
    
      h1,
      h2,
      h3,
      h4,
      h5,
      h6 {
        color: ${(props) => props.theme.colors.primaryDarkest};
        margin: 1rem 0;
      }
      h1 {
        font-size: 1.95rem;
        line-height: 2rem;
        font-weight: 400;
      }
      h2 {
        font-size: 1.5rem;
        line-height: 1.75rem;
        font-weight: 400;
      }
      h3 {
        font-size: 1.35rem;
        line-height: 1.625rem;
        font-weight: 400;
      }
      h4 {
        font-size: 1.15rem;
        line-height: 1.5rem;
        font-weight: 400;
      }
    
      h5 {
        font-size: 1.095rem;
        line-height: 1.375rem;
        font-weight: 700;
      }
    
      h6 {
        font-size: 1rem;
        line-height: 1.5rem;
        font-weight: 700;
      }
     `;
    

    In the above code, you created a theme object with some basic CSS and used the createGlobalStyle function from styled-components to add global styles to the app.

    To use the theme and global styles, you need to create a file named gatsby-browser.js in the project’s root directory. Run the following command in your project’s root directory.

    touch gatsby-browser.js
    

    Add the following code to the gatsby-browser.js file.

    // gatsby-browser.js
    import React from "react";
    import { GlobalStyles, theme } from "./src/themes/global-styles";
    import { ThemeProvider } from "styled-components";
    
    export const wrapRootElement = ({ element }) => (
      <ThemeProvider theme={theme}>
        <GlobalStyles />
        {element}
      </ThemeProvider>
    );
    

    Here you use the wrapRootElement function from gatsby to wrap the entire root element with ThemeProvider wrapper component from styled-components, which provides a theme to all React components underneath itself via the context API.

    You pass the theme object created in the global-styles.js file to the theme prop of the ThemeProvider component.

    Then you pass the GlobalStyles component at the top of the React tree.

    You can access the theme object attributes as props in any of the component like ${props => props.theme.fontSizes.medium};.

    Start your development server by running the gatsby develop command and head over to http://localhost:8000/.

    Global Styles

    You will notice that the background-color, font, etc., of the app, has changed according to the CSS in the GlobalStyles component.

    Creating a Container Component

    Your Starter should have consistent styling and a common layout throughout the different pages or it will seem messy and result in bad user experience. In this section, you will create a Container component that will wrap your entire app with a navbar and a footer. The Container component is the common layout of your starter and contains other components such as a Footer component and a Navbar component.

    Although it’s not a requirement for a Gatsby Starter to have such a Container component, you will find that by splitting code into different components, it becomes easier to manage and helps remove repeated code. For example, every page in your starter will have a footer, so instead of repeating the same code, make and use a separate Footer component in its place.

    In the src directory, create a file named Container.js inside a folder named components by running the following command.

    cd src
    mkdir components
    cd components
    touch Container.js
    

    Add the following code to Container.js.

    // Container.js
    import React from "react";
    import styled from "styled-components";
    
    const ContainerWrapper = styled.div`
      height: 100%;
      display: block;
      margin: auto;
      max-width: 640px;
    `;
    export const Container = ({ children }) => {
      return <ContainerWrapper>{children}</ContainerWrapper>;
    };
    

    This Container component defines the layout of your app.

    Update the pages/index.js file to include the Container component like this.

    // index.js
    import React from "react";
    import { Container } from "../components/Container";
    
    export default function Home() {
      return <Container>Hello world!</Container>;
    }
    

    With your development server still running, navigate to http://localhost:8000/ to see your app with the new container.

    Container Component

    Creating a Navbar Component

    The next step is to create a navbar component and use it inside the Container component.

    Run the following command inside the components directory to create the Nav.js file.

    touch Nav.js
    

    You will also need to install react-icons to add icons to your blog. This package includes some of the most popular icon libraries like Ant Design Icons, Bootstrap Icons, and more. Having different icons in your Starter can result in better user experience, and social media icons (Instagram, LinkedIn, Facebook) can help your readers connect with you on different platforms.

    Install react-icons by running the following command.

    npm install react-icons
    

    Add the following code to Nav.js.

    // Nav.js
    import React from "react";
    import { Link } from "gatsby";
    import { FaRegMoon } from "react-icons/fa";
    import styled from "styled-components";
    
    const NavWrapper = styled.nav`
      font-size: 1.5rem;
      display: flex;
      justify-content: space-between;
      align-items: center;
      flex-direction: row;
      margin-top: 0.3rem 0.6rem;
    
      a {
        text-decoration: none;
        font-family: Satisfy;
        color: #6a5acd;
      }
      svg {
        font-size: 1rem;
        color: #4a4656;
        &:hover {
        color: #000;
        }
      }
      @media only screen and (max-width: 480px) {
        font-size: 1rem;
      }
    `;
    export const Nav = () => {
      return (
        <NavWrapper>
        {/* Replace with your Name */}
        <Link to="/">Ashutosh K Singh</Link>
        <FaRegMoon />
        </NavWrapper>
      );
    };
    

    In the above code, you use the Link component to add a link to the homepage, i.e., to the / page. Make sure to replace the text inside the Link component with your name. You can also use a logo instead of text.

    Update the Container.js file to include this component like this.

    // Container.js
    import React from "react"
    import styled from "styled-components"
    import { Nav } from "./Nav"
    
    const ContainerWrapper = styled.div`
      height: 100%;
      display: block;
      margin: auto;
      max-width: 640px;
    `
    export const Container = ({ children }) => {
      return (
        <ContainerWrapper>
        {" "}
        <Nav />
        {children}
        </ContainerWrapper>
      )
    }
    

    Head over to http://localhost:8000/ to see your app with the new navbar component and icon.

    Navbar in Container

    Creating a Footer Component

    The next step is to create a Footer.js file to add a footer to the Container component. Run the following command inside the components directory to create the Footer.js file.

    touch Footer.js
    

    Add the following code to the Footer.js file.

    import React from "react";
    import {
      FaRegEnvelope,
      FaGithub,
      FaLinkedinIn,
      FaTwitter,
      FaFacebookF,
      FaInstagram,
      FaRss,
    } from "react-icons/fa";
    import styled from "styled-components";
    
    const FooterWrapper = styled.footer`
      min-height: 12rem;
      justify-content: center;
      align-items: center;
      text-align: center;
      padding: 3rem 0;
      margin-bottom: 2rem;
    
      p {
        font-size: 0.8rem;
        font-weight: 300;
      }
      ul {
        text-align: center;
        li {
        display: inline-block;
        a {
            svg {
            color: #4a4656;
            &:hover {
                color: #8a2be2;
            }
            }
        }
        }
        li:not(:last-child) {
        margin-right: 40px;
        }
      }
    `;
    export const Footer = () => {
      const config = {
        email: `admin@ashusingh.me`,
        githubUsername: `lelouchB`,
        twitterUsername: `noharashutosh`,
        instagramUsername: `singhashutoshk`,
        siteUrl: `http://localhost:8000/`,
      };
      const linkedinUrl = `https://www.linkedin.com/in/ashutosh-singh-550a6a178`;
      const instagramUrl = `https://www.instagram.com/${config.instagramUsername}`;
      const mailToUrl = `mailto:${config.email}?Subject=Hello`;
      const twitterUrl = `https://twitter.com/${config.twitterUsername}`;
      const facebookURL = `https://facebook.com/`;
      const githubUrl = `https://github.com/${config.githubUsername}`;
      const rssFeed = `${config.siteUrl}/rss.xml`;
    
      return (
        <FooterWrapper>
        <ul>
            <li>
            <a href={mailToUrl} target="_top" title="Email">
                <FaRegEnvelope />
            </a>
            </li>
    
            <li>
            <a
                href={githubUrl}
                target="_blank"
                title="Contact Ashutosh at GitHub"
                rel="noopener noreferrer"
            >
                <FaGithub />
            </a>
            </li>
            <li>
            <a
                href={linkedinUrl}
                target="_blank"
                title="Contact Ashutosh on Linkedin"
                rel="noopener noreferrer"
            >
                <FaLinkedinIn />
            </a>
            </li>
            <li>
            <a
                href={twitterUrl}
                target="_blank"
                title="Contact Ashutosh on Twitter"
                rel="noopener noreferrer"
            >
                <FaTwitter />
            </a>
            </li>
            <li>
            <a
                href={facebookURL}
                target="_blank"
                title="Contact Ashutosh on Facebook"
                rel="noopener noreferrer"
            >
                <FaFacebookF />
            </a>
            </li>
    
            <li>
            <a
                href={instagramUrl}
                target="_blank"
                title="Contact Ashutosh on Instagram"
                rel="noopener noreferrer"
            >
                <FaInstagram />
            </a>
            </li>
            <li>
            <a
                href={rssFeed}
                target="_blank"
                title="RSS Feed"
                rel="noopener noreferrer"
            >
                <FaRss />
            </a>
            </li>
        </ul>
        <p>
            Handcrafted with{" "}
            <span role="img" aria-label="white_heart">
            🤍
            </span>{" "}
            by me
        </p>
        </FooterWrapper>
      );
    };
    

    The code above first imports all the different icons from react-icons and then creates an unordered list with all the icons. Inside the <li> tag, it uses <a>s to add different social links like Facebook, Instagram, etc.

    One thing you may notice is the rssFeed, which points to http://localhost:8000/rss.xml. You can ignore this for now, but this is the route where you will create the RSS feed for your blog later in this tutorial.

    Remember to replace the above links with your social handles and URLs.

    Update the Container.js file to include this Footer component like this.

    // Container.js
    import React from "react";
    import styled from "styled-components";
    import { Nav } from "./Nav";
    import { Footer } from "./Footer";
    
    const ContainerWrapper = styled.div`
      height: 100%;
      display: block;
      margin: auto;
      max-width: 640px;
    `;
    export const Container = ({ children }) => {
      return (
        <ContainerWrapper>
        <Nav />
        {children}
        <Footer />
        </ContainerWrapper>
      );
    };
    

    Head over to http://localhost:8000/. Here is how your app will look now.

    Footer in Container

    Creating a Bio Component

    In this section, you will create a Bio component with an image and some information about the author. Having a bio will help readers connect with you on a more personal level. You can tell them about yourself, why you write articles, and where they can find your other work. A good bio section helps build a sense of trust between content creators like bloggers and the readers.

    In this section, I’ll refer to an image named myAvatar.png inside the src/images directory. You can either replace this with your avatar or download the image used in this tutorial from here.

    As you may remember, you installed gatsby-plugin-sharp and gatsby-transformer-sharp plugins in the above sections to optimize your images. Now you need to install the gatsby-image component to make use of those plugins.

    Run the following command in your project’s root directory to install gatsby-image.

    npm install gatsby-image
    

    Run the following command inside the components folder to create a Bio.js file.

    touch Bio.js
    

    Add the following code to Bio.js.

    // Bio.js
    import React from "react";
    import styled from "styled-components";
    import Img from "gatsby-image";
    import { useStaticQuery, graphql } from "gatsby";
    import { GiDove } from "react-icons/gi";
    
    const BioWrapper = styled.div`
      display: flex;
      flex-direction: column;
      align-items: center;
      justify-content: center;
      text-align: center;
      margin-top: 0.4rem;
      svg {
        color: purple;
        font-size: 2rem;
      }
      h2 {
        margin-bottom: 0.5rem;
      }
      h3 {
        margin: 0.8rem 0;
        font-weight: 600;
      }
    `;
    export const Bio = () => {
      const data = useStaticQuery(graphql`
        query {
        imageSharp(fixed: { originalName: { eq: "myAvatar.png" } }) {
            id
            fixed(width: 250, height: 250) {
            ...GatsbyImageSharpFixed
            }
        }
        }
      `);
      return (
        <BioWrapper>
        <Img
            fixed={data.imageSharp.fixed}
            style={{ borderRadius: "50%", left: "0", top: "0" }}
            alt="My Avatar"
        />
        <h2>
            Hey Stranger <GiDove />
        </h2>
    
        <br />
        <p>
            My name is <b>Ashutosh</b>! I'm a JavaScript Developer & Technical
            Writer. I develop awesome stuff with JavaScript and love to write about
            them.
        </p>
        <h3>Latest Posts</h3>
        </BioWrapper>
      );
    };
    

    In the above code, you added a Hey Stranger message and a short bio. Make sure to update these paragraphs with your information.

    Take a look at how you are fetching the myAvatar.png image.

     const data = useStaticQuery(graphql`
        query {
        imageSharp(fixed: { originalName: { eq: "myAvatar.png" } }) {
            id
            fixed(width: 250, height: 250) {
            ...GatsbyImageSharpFixed
            }
        }
        }
      `)
    

    Here you are using the useStaticQuery hook and graphql imported from gatsby. useStaticQuery hook does not accept variables (hence the name “static”), and is used here to find with originalName: { eq: "myAvatar.png" }. Replace myAvatar.png with your image name. You can read more about ...GatsbyImageSharpFixed here.

    You then pass this data.imageSharp.fixed to the Img component from gatsby-image like this.

    <Img
      fixed={data.imageSharp.fixed}
      style={{ borderRadius: "50%", left: "0", top: "0" }}
      alt="Avatar"
    />
    

    Update the pages/index.js file to include this Bio component.

    // index.js
    import React from "react";
    import { Container } from "../components/Container";
    import { Bio } from "../components/Bio";
    
    export default function Home() {
      return (
        <Container>
        <Bio />
        </Container>
      );
    }
    

    Restart your development server and head over to http://localhost:8000/ to see the bio component on your blog’s home page:

    Bio Component

    Creating an SEO Component

    SEO stands for Search Engine Optimization, it means that you are optimizing your website to rank better in the results on search engines like Google. There are many factors that affect your site rank on Google or other search engines like the web crawl accessibility, optimized keywords on each page, fast loading speeds, good user experience, and more.

    Gatsby can help your site rank and perform better in search engines. Because Gatsby pages are server-rendered, the crawlers are easily able to access your site. You can also add meta information so the search engines can understand your content and know where to show your pages in search results. You can read more about SEO for Gatsby here.

    In this section, you will create an SEO component that adds basic meta-tags like the title, description, or lang, etc., of the page in the <head> element.

    Run the following command to install the gatsby-plugin-react-helmet plugin and the react-helmet library.

    npm install gatsby-plugin-react-helmet react-helmet
    

    Stop your development server and update the gatsby-config.js. Make sure to update the fields with your social handles and information.

    // gatsby-config.js
    module.exports = {
      siteMetadata: {
        title: `Gatsby Starter Blog`,
        author: {
        name: `Ashutosh Kumar Singh`,
        },
        description: `Gatsby Starter Blog built with MDX and Styled Components.`,
        image: `/cover.png`,
        siteUrl: `https://lelouchb-gatsby-starter-blog-demo.netlify.app/`,
        social: {
        twitter: `@noharashutosh`,
        },
      },
      plugins: [
        // plugins
        `gatsby-plugin-react-helmet`,
      ],
    };
    

    One thing that you might notice is the cover.png image in the image field. This image will be used in the social card preview of the site. This image is stored inside the static directory. You can either use a customized image or download the cover image used in this tutorial from here.

    Head over to localhost:8000/__graphql and paste the following query in the left tab.

    {
      site {
        siteMetadata {
        title
        description
        social {
            twitter
        }
        author {
            name
        }
        image
        siteUrl
        }
      }
    }
    

    Hit the run button. You will see an output similar to this.

    {
      "data": {
        "site": {
        "siteMetadata": {
            "title": "Gatsby Starter Blog",
            "description": "Gatsby Starter Blog built with MDX and Styled Components.",
            "social": {
            "twitter": "@noharashutosh"
            },
            "author": {
            "name": "Ashutosh Kumar Singh"
            },
            "image": "/cover.png",
            "siteUrl": "https://lelouchb-gatsby-starter-blog.netlify.app"
        }
        }
      },
      "extensions": {}
    }
    

    This is the query that you will use with the useStaticQuery hook in the Seo.js file.

    Create a file named Seo.js under the components directory.

    cd components
    touch Seo.js
    

    Add the following code to the Seo.js file.

    // Seo.js
    import React from "react";
    import PropTypes from "prop-types";
    import { Helmet } from "react-helmet";
    import { useStaticQuery, graphql } from "gatsby";
    
    export const SEO = ({ title, lang, meta, description }) => {
      const { site } = useStaticQuery(
        graphql`
        query {
            site {
            siteMetadata {
                title
                description
                social {
                twitter
                }
                author {
                name
                }
                image
                siteUrl
            }
            }
        }
        `
      );
    
      const metaDescription = description || site.siteMetadata.description;
      const defaultTitle = site.siteMetadata?.title;
      const siteUrl = site.siteMetadata?.siteUrl;
      const image = site.siteMetadata?.image;
    
      return (
        <Helmet
        htmlAttributes={{
            lang,
        }}
        title={title}
        titleTemplate={defaultTitle ? `%s | ${defaultTitle}` : null}
        meta={[
            {
            name: `title`,
            content: title,
            },
            {
            name: `description`,
            content: metaDescription,
            },
            {
            property: `og:title`,
            content: title,
            },
            {
            property: `og:description`,
            content: metaDescription,
            },
            { property: "og:locale", content: lang },
            {
            property: "og:site_name",
            content: "Gatsby Starter Blog",
            },
            {
            property: `og:type`,
            content: `website`,
            },
            {
            property: `og:image`,
            content: `${siteUrl}${image}`,
            },
            {
            name: `twitter:card`,
            content: `summary_large_image`,
            },
            {
            name: `twitter:creator`,
            content: site.siteMetadata?.social?.twitter || ``,
            },
            {
            name: `twitter:title`,
            content: title,
            },
            {
            name: `twitter:description`,
            content: metaDescription,
            },
            {
            name: `twitter:image`,
            content: `${siteUrl}${image}`,
            },
        ].concat(meta)}
        />
      );
    };
    
    SEO.defaultProps = {
      lang: `en`,
      meta: [],
      description: ``,
    };
    
    SEO.propTypes = {
      description: PropTypes.string,
      lang: PropTypes.string,
      meta: PropTypes.arrayOf(PropTypes.object),
      title: PropTypes.string.isRequired,
    };
    

    You will not notice any change in the app since you have created the SEO component, but have not used it in any of the pages yet. You’ll see how to incorporate your SEO component in the next section.

    The Tech Content Manger's Playbook

    Displaying Posts on Home Page

    In this section, you will create dummy posts for the starter blog, query them in the pages/index.js file, and show them on the homepage.

    For this tutorial, I chose to create a separate folder for each post. This folder includes the markdown file and the accompanying images, videos, etc. This is not strictly necessary, but having a single folder for all posts can become a little hard to manage as your blog scales and you have more images and media assets to incorporate.

    Inside the src/posts directory, create a folder named my-first-post and inside it create a file named index.mdx. Add sample data to index.mdx from here.

    The content inside --- is the frontmatter of the post and includes title, slug, date, tags, and excerpt of the post. You can also include other fields like keywords, cover_image, author, etc., in the frontmatter. You can refer to Gatsby documentation for the GraphQL query to fetch data from this post.

    But first, you need to create a PostContainer component that will take all the fetched data as props and show them in a more styled manner. Run the following code to create a PostContainer component under the components directory.

    cd src
    cd components
    touch PostContainer.js
    

    Add the following code to the PostContainer.js file.

    import React from "react";
    import styled from "styled-components";
    import { Link } from "gatsby";
    
    const PostContainerWrapper = styled.div`
      margin-top: 1rem;
      padding: 1rem;
      display: flex;
      flex-direction: column;
      align-items: center;
      background-color: #fafafa;
      box-shadow: 2px 3px 4px 6px #ccc;
      border-radius:10px;
      text-align: center;
    
      h2 {
        font-size: 1.6rem;
        margin: 0.75rem 0;
        font-weight: 500;
        color: {$props=>props.theme.colors.primaryDarkest};
      }
      p {
        font-size: 0.8rem;
      }
      span {
        font-size: 0.75rem;
        margin: 0.2rem 0;
        font-weight: 400;
        color: #949494;
      }
    `;
    
    const ButtonWrapper = styled((props) => <Link {...props} />)`
      padding: 0.45rem 0.5rem;
      margin-top: 0.4rem;
      font-size: 0.9rem;
      background-color: #6a5acd;
      border-radius: 0.5rem;
      font-weight: 600;
      color: white;
      text-decoration: none;
    `;
    
    export const PostContainer = ({ title, date, excerpt, slug }) => {
      return (
        <PostContainerWrapper>
        <h2>{title}</h2>
        <span>{date}</span>
        <p>{excerpt}</p>
        <ButtonWrapper to={`posts/${slug}`}>Read More</ButtonWrapper>
        </PostContainerWrapper>
      );
    };
    

    In the above code, you passed title, date, excerpt, slug to the PostContainer component as props. You will notice that theRead More link does not work since you have not yet created the dynamic route it points to.

    Update the index.js file to show the posts.

    import React from "react";
    import { graphql } from "gatsby";
    import { Container } from "../components/Container";
    import { SEO } from "../components/Seo";
    import { PostContainer } from "../components/PostContainer";
    import { Bio } from "../components/Bio";
    
    export default function Home({ data }) {
      const posts = data.allMdx.edges;
      if (posts.length === 0) {
        return (
        <Container>
            <SEO title="All posts" />
            <Bio />
            <p>
            No blog posts found. Add markdown posts to "src/posts" (or the
            directory you specified for the "gatsby-source-filesystem" plugin in
            gatsby-config.js).
            </p>
        </Container>
        );
      }
      return (
        <Container>
        <SEO title="Gatsby Starter Blog" />
        <Bio />
    
        {posts.map((post) => (
            <PostContainer
            key={post.node.id}
            date={post.node.frontmatter.date}
            title={post.node.frontmatter.title}
            slug={post.node.frontmatter.slug}
            excerpt={post.node.frontmatter.excerpt}
            />
        ))}
        </Container>
      );
    }
    
    export const pageQuery = graphql`
      query AllPostsQuery {
        allMdx(sort: { fields: frontmatter___date, order: DESC }) {
        edges {
            node {
            frontmatter {
                slug
                title
                excerpt
                date(formatString: "MMMM DD, YYYY")
            }
            id
            }
        }
        }
      }
    `;
    

    The above code uses the GraphQL query discussed above to fetch posts data and pass them in the Home component as data prop. Then it checks if there are any posts present using an if statement and posts.length === 0. If posts.length > 0 then you use the .map() method on the posts array and pass the data to the PostContainer component.

    Navigate to http://localhost:8000/ in the browser to see a snippet of your first post on the homepage.

    Homepage with one post

    Create another post inside the posts directory using your command line.

    cd src
    cd posts
    mkdir markdown-syntax-guide
    cd markdown-syntax-guide
    touch index.mdx
    

    Copy the code from GitHub to create another post named markdown-syntax-guide.

    The above markdown file uses an image named mac.jpg stored inside the markdown-syntax-guide folder. You can download this image from here.

    After saving this markdown file, the homepage will automatically update to show this post.

    Homepage with two posts

    Creating Pages from Data

    Next, you will create individual pages for each of your posts.

    Stop your development server and run the following command to create a gatsby-node.js file inside your project’s root directory.

    touch gatsby-node.js
    

    This file is used to create pages dynamically, add nodes in GraphQL, or respond to events during the build lifecycle. You can read more about it here.

    Add the following code in gatsby-node.js.

    // gatsby-node.js
    const path = require(`path`);
    
    exports.createPages = async function ({ actions, graphql }) {
      const { createPage } = actions;
      const { data } = await graphql(`
        {
        allMdx(sort: { fields: frontmatter___date, order: DESC }) {
            edges {
            node {
                frontmatter {
                slug
                }
                id
            }
            }
        }
        }
      `);
    
      data.allMdx.edges.forEach((post) => {
        const slug = post.node.frontmatter.slug;
    
        createPage({
        path: `posts/${slug}`,
        component: path.resolve(`./src/templates/blog-post.js`),
        context: { id: post.node.id },
        });
      });
    };
    

    The above code uses a template file named blog-post.js inside the src/templates directory to create the posts’ page.

    Run the following command to create the template blog-post.js file.

    cd src
    mkdir templates
    cd templates
    touch blog-post.js
    

    Add the following code to blog-post.js.

    // blog-post.js
    import React from "react";
    import { graphql } from "gatsby";
    import { MDXRenderer } from "gatsby-plugin-mdx";
    import { Container } from "../components/Container";
    import { SEO } from "../components/Seo";
    import styled from "styled-components";
    
    const PostWrapper = styled.div`
      padding: 1rem;
      span {
        font-weight: 500;
      }
    
      p {
        font-size: 1.125 rem;
        font-weight: 400;
        color: #2e2d2f;
        margin: 0.625rem 0;
      }
    
      a {
        color: blue;
        text-decoration: underline;
      }
      strong {
        font-weight: 700;
      }
      em: {
        font-style: italic;
      }
      del {
        text-decoration: line-through;
      }
      blockquote p {
        font-style: italic;
        font-size: 1.125rem;
        line-height: 1.75rem;
        text-align: center;
        max-width: 36rem;
        margin: 3rem auto;
      }
      ul,
      ol {
        color: #2e2d2f;
        margin: 1rem 0 1rem 2rem;
      }
    
      li {
        margin: 0.25rem 0;
      }
    
      table {
        width: 100%;
        border-spacing: 0.25rem;
        border-collapse: collapse;
        font-size: 1rem;
      }
      table,
      th,
      td {
        border: 1px solid #000;
      }
      th {
        font-weight: 700;
      }
      td {
        padding: 0.15rem;
        text-align: left;
      }
    `;
    
    const TitleWrapper = styled.div`
      text-align: left;
      font-size: 2.1rem;
      font-family: JetBrains Mono;
      font-weight: 500;
      background: #0f2027;
      background: -webkit-linear-gradient(to right, #2c5364, #203a43, #0f2027);
      background: linear-gradient(to right, #2c5364, #203a43, #0f2027);
      -webkit-background-clip: text;
      -webkit-text-fill-color: transparent;
    `;
    
    const Title = ({ title }) => {
      return <TitleWrapper>{title}</TitleWrapper>;
    };
    
    const DateWrapper = styled.span`
      text-align: left;
      font-size: 0.6rem;
      font-weight: 500;
    `;
    
    const Date = ({ date }) => {
      return <DateWrapper>{date}</DateWrapper>;
    };
    const TimeToReadWrapper = styled.span`
      text-align: left;
      margin-left: 1rem;
      font-size: 0.6rem;
      font-weight: 500;
    `;
    
    const TimeToRead = ({ timeToRead }) => {
      return (
        <TimeToReadWrapper>
        {timeToRead} {timeToRead > 1 ? `MINUTES` : `MINUTE`} TO READ
        </TimeToReadWrapper>
      );
    };
    
    const TagsWrapper = styled.div`
      display: flex;
      flex-direction: row;
      max-width: 480px;
      font-size: 0.6rem;
      font-weight: 600;
      color: #ff0099;
    `;
    const TagWrapper = styled.span`
      padding-right: 0.3rem;
      cursor: pointer;
    `;
    
    const Tags = ({ tags }) => {
      return (
        <TagsWrapper>
        {tags.map((tag) => (
            <TagWrapper key={tag}>{tag}</TagWrapper>
        ))}
        </TagsWrapper>
      );
    };
    
    const singlePost = ({ data }) => {
      return (
        <Container>
        <SEO
            title={data.mdx.frontmatter.title}
            description={data.mdx.frontmatter.excerpt}
        />
        <Title title={data.mdx.frontmatter.title} />
        <Date date={data.mdx.frontmatter.date} />
        <TimeToRead timeToRead={data.mdx.timeToRead} />
        <Tags tags={data.mdx.frontmatter.tags} />
        <PostWrapper>
            <MDXRenderer>{data.mdx.body}</MDXRenderer>
        </PostWrapper>
        </Container>
      );
    };
    
    export default singlePost;
    
    export const pageQuery = graphql`
      query SinglePostQuery($id: String!) {
        mdx(id: { eq: $id }) {
        body
        frontmatter {
            date(formatString: "MMMM DD, YYYY")
            excerpt
            slug
            title
            tags
        }
        timeToRead
        }
      }
    `;
    

    Restart your development server and head over to http://localhost:8000/ and click on Read More on any post.

    Alternatively, head over to http://localhost:8000/posts/markdown-syntax-guide for the post titled Markdown Syntax Guide.

    Markdown Syntax Guide post

    Creating a 404 Page

    A 404 page is the page that users see when they try to visit a non-existing URL on your website or when the server cannot find the requested URL. A good 404 page contributes to a better user experience by reducing bounce rate and maintains consistent branding and layout throughout the website.

    It can show users that the URL they are trying to access doesn’t exist and redirect them somewhere else (like the home page), or it can show a few popular blog posts to help visitors find something else they might like. Gatsby comes with a prebuilt 404 page for non-existing routes, but it’s probably a good idea to add a custom 404 page to your blog.

    Run the following command to create 404.js inside the src/pages directory.

    cd src/pages
    touch 404.js
    

    Add the following code to the 404.js file.

    // 404.js
    import React from "react";
    import { Container } from "../components/Container";
    import { SEO } from "../components/Seo";
    
    const NotFound = () => {
      return (
        <Container>
        <SEO title="404 Not Found" />
        <h1>NOT FOUND</h1>
        <p>You just hit a route that doesn&#39;t exist...</p>
        </Container>
      );
    };
    
    export default NotFound;
    

    The above code uses the Container component to create a 404 page with a simple text that this route doesn’t exist. Head over to http://localhost:8000/404/ to see your new 404 page in action.

    404 Not Found

    Adding a Manifest to Your Gatsby Starter

    A Progressive Web Application enhances user experience based on the browser capabilities, makes your website installable as an app on mobile devices, supports caching and offline usage. A PWA also adds support for push notifications and has access to multiple native APIs such as camera, geo location, payments, etc.

    Gatsby’s manifest plugin configures Gatsby to create a manifest.webmanifest file on every site build. Run the following command to install the gatsby-plugin-manifest plugin.

    npm install gatsby-plugin-manifest
    

    Stop your development server and update the gatsby-config.js file to include the gatsby-plugin-manifest plugin like this.

    {
      resolve: `gatsby-plugin-manifest`,
      options: {
        name: `Gatsby Starter Blog`,
        short_name: `Gatsby`,
        start_url: `/`,
        lang: `en`,
        background_color: `#f7f0eb`,
        theme_color: `purple`,
        display: `standalone`,
        icon: `src/images/myAvatar.png`,
        icon_options: {
            purpose: `any maskable`,
            },
      },
    },
    

    Make sure to update the above fields with your blog data and color scheme. myAvatar.png is an image inside the src/images folder, so you can replace this with a photo of your choice.

    Restart your development server by running the gatsby develop command in the terminal. Head over to http://localhost:8000/manifest.webmanifest where you will see a manifest file like this.

    {
      "name": "Gatsby Starter Blog",
      "short_name": "Gatsby",
      "start_url": "/",
      "lang": "en",
      "background_color": "#f7f0eb",
      "theme_color": "purple",
      "display": "standalone",
      "cacheDigest": "d6541ebfd3ce17cfc7741d27bb585271",
      "icons": [
        {
        "purpose": "any maskable",
        "src": "icons/icon-48x48.png?v=d6541ebfd3ce17cfc7741d27bb585271",
        "sizes": "48x48",
        "type": "image/png"
        },
        {
        "purpose": "any maskable",
        "src": "icons/icon-72x72.png?v=d6541ebfd3ce17cfc7741d27bb585271",
        "sizes": "72x72",
        "type": "image/png"
        },
        {
        "purpose": "any maskable",
        "src": "icons/icon-96x96.png?v=d6541ebfd3ce17cfc7741d27bb585271",
        "sizes": "96x96",
        "type": "image/png"
        },
        {
        "purpose": "any maskable",
        "src": "icons/icon-144x144.png?v=d6541ebfd3ce17cfc7741d27bb585271",
        "sizes": "144x144",
        "type": "image/png"
        },
        {
        "purpose": "any maskable",
        "src": "icons/icon-192x192.png?v=d6541ebfd3ce17cfc7741d27bb585271",
        "sizes": "192x192",
        "type": "image/png"
        },
        {
        "purpose": "any maskable",
        "src": "icons/icon-256x256.png?v=d6541ebfd3ce17cfc7741d27bb585271",
        "sizes": "256x256",
        "type": "image/png"
        },
        {
        "purpose": "any maskable",
        "src": "icons/icon-384x384.png?v=d6541ebfd3ce17cfc7741d27bb585271",
        "sizes": "384x384",
        "type": "image/png"
        },
        {
        "purpose": "any maskable",
        "src": "icons/icon-512x512.png?v=d6541ebfd3ce17cfc7741d27bb585271",
        "sizes": "512x512",
        "type": "image/png"
        }
      ]
    }
    

    Now your Gatsby starter will function as a progressive web app, and users will be able to save it to their device’s home screen. You can also enable offline mode, push notifications, or any of the other features PWAs offer.

    Adding a Sitemap

    A sitemap is an XML file that lists a website’s essential pages, making sure search engines (such as Google) can find and crawl them all. In this section, I’ll show you how to add a sitemap to your Gatsby starter.

    Stop your development server and install the gatsby-plugin-sitemap plugin by running the following command.

    npm install gatsby-plugin-sitemap
    

    Update gatsby-config.js to include this gatsby-plugin-sitemap plugin.

    // gatsby-config.js
    module.exports = {
      siteMetadata: {
        // siteMetadata
      },
      plugins: [
        // plugins
        {
        resolve: `gatsby-plugin-sitemap`,
        options: {
            output: `/sitemap.xml`,
            query: `
                {
                site {
                    siteMetadata {
                    siteUrl
                    }
                }
    
                allSitePage {
                    nodes {
                    path
                    }
                }
            }
            `,
            resolveSiteUrl: ({ site, allSitePage }) => {
            return site.siteMetadata.siteUrl;
            },
            serialize: ({ site, allSitePage }) =>
            allSitePage.nodes.map((node) => {
                return {
                url: `${site.siteMetadata.siteUrl}${node.path}`,
                changefreq: `daily`,
                priority: 0.7,
                };
            }),
        },
        },
      ],
    };
    

    The above GraphQL query fetches all the metadata of your blog as well all the existing routes. If you run this query in the GraphiQL playground, you will see an output similar to this.

    {
      "data": {
        "site": {
        "siteMetadata": {
            "siteUrl": "https://lelouchb-gatsby-starter-blog.netlify.app"
        }
        },
        "allSitePage": {
        "nodes": [
            {
            "path": "/posts/markdown-syntax-guide"
            },
            {
            "path": "/posts/my-first-post"
            },
            {
            "path": "/dev-404-page/"
            },
            {
            "path": "/404/"
            },
            {
            "path": "/"
            },
            {
            "path": "/404.html"
            }
        ]
        }
      },
      "extensions": {}
    }
    

    You can test this sitemap by running the following command in the project’s root directory to create and serve your production build.

    gatsby build && gatsby serve
    

    Head over to http://localhost:9000/sitemap.xml

    Sitemap.xml

    Adding an RSS Feed

    Let’s say that I am an avid reader of your blog and I want to be notified of every new article that you publish so one way would be to visit your blog everyday and manually check for new articles. This task is tedious even for one blog but what if I want to be notified of many blog websites, manually checking is out of the box.

    This is where the RSS feed comes to save the day, readers can use a RSS feed reader like feedly, Miniflux, RSS Owl, etc. and subscribe to your RSS feeds and be updated with latest articles.

    Here is an example of someone subscribing to this starter blog RSS feed.

    Subscribe RSS Feed on Feedly

    Earlier in this tutorial, you added an RSS feed icon in the footer that points to the /rss.xml route that didn’t exist yet. To create the RSS feed, you will use the gatsby-plugin-feed plugin.

    npm install gatsby-plugin-feed
    

    Stop your development server and update the gatsby-config.js file to include the gatsby-plugin-feed plugin.

    {
      resolve: `gatsby-plugin-feed`,
      options: {
        query: `
        {
            site {
            siteMetadata {
                title
                description
                siteUrl
                site_url: siteUrl
            }
            }
        }
        `,
        feeds: [
        {
            serialize: ({ query: { site, allMdx } }) => {
            return allMdx.edges.map(edge => {
                return Object.assign({}, edge.node.frontmatter, {
                description: edge.node.excerpt,
                date: edge.node.frontmatter.date,
                url:
                    site.siteMetadata.siteUrl +
                    `/posts/` +
                    edge.node.frontmatter.slug,
                guid:
                    site.siteMetadata.siteUrl +
                    `/posts/` +
                    edge.node.frontmatter.slug,
                custom_elements: [{ "content:encoded": edge.node.html }],
                })
            })
            },
            query: `
            {
                allMdx(
                sort: { order: DESC, fields: [frontmatter___date] },
                ) {
                edges {
                    node {
                    excerpt
                    html
                    frontmatter {
                        title
                        date
                        slug
    
                    }
                    }
                }
                }
            }
            `,
            output: "/rss.xml",
            title: "Gatsyby Starter Blog RSS Feed",
        },
        ],
      },
    },
    

    {% endraw %}

    There are two GraphQL queries in the above code: one for the site metadata and the other for all the posts. Like gatsby-plugin-sitemap, you will need to create a production build to see this plugin in action.

    gatsby build && gatsby serve
    

    Head over to http://localhost:9000/rss.xml, to see how the RSS feed will look.

    RSS Feed

    Make sure to update the siteUrl in the gatsby-config.js file before deploying your blog so that the RSS feed’s URL is correct.

    Finishing the Starter

    At this point, you have created a working Gatsby blog, but you still have one more step to make it a shareable Gatsby starter. According to official Gatsby documentation, a starter should have a few specific files to work properly.

    First, add a README.md to your project. This should contain a screenshot of the starter and a thorough explanation for users to configure and customize it.

    Next, add a License to your project. BSD Zero Clause is generally preferred for Gatsby starters.

    Finally, update the package.json file to include the license, description, author name, version, etc. You can see this starter’s package.json here.

    Now you can submit your starter to the official Gatsby registry or simply share your starter with other developers on GitHub.

    Summary

    In this tutorial, you learned how to build a Gatsby starter from scratch using MDX, Styled Components, and various Gatsby plugins. This starter included blog posts, an author bio, search engine optimization plugins, an RSS feed, and more. Using this starter, other developers will be able to build their own Gatsby projects much faster.

    You can explore the source code of this project on GitHub, or if you want to use the starter created here, run the following in your terminal.

    gatsby new my-gatsby-blog https://github.com/lelouchB/gatsby-starter-blog
    

    If you’re inspired to add features yourself, please do share and tag me – I’d love to hear about them!

  • Creating Hugo Themes

    Creating Hugo Themes

    Lately, developers are adopting static site generators like Hugo to quickly deliver content to their audience. Frameworks like these cut down on issues that pop up with scalability, version control, managing dependencies, and performance.

    Hugo in particular offers tons of features right out of the box for creating fast, modern, and secure static websites. It’s written in Go, a powerful server-side scripting language, and offers a clean workflow for creating landing pages, portfolios, documentation, or blogs without a lot of extra layers of complexity.

    Hugo also has plenty of themes ready to go right away, but a prebuilt theme probably isn’t going to match your brand’s vision. Most likely, you’re going to want a custom theme, something that affords you fine-grained control over things like color schemes, typography, and UI components.

    In this article, I’ll walk you through how to quickly customize your Hugo site for your desired brand and aesthetic by building your own theme.

    How to Create a Multi-author Hugo Blog Theme

    Specifically, I’ll demonstrate how to create a multi-author blog theme in Hugo. I’ll cover paginating multiple blog posts, adding tags or categories, setting up support for multiple authors, and adding some SEO considerations to give your site more presence on the web.

    Spinning Up a Site

    Once you have Hugo installed (refer to the complete installation guide here) and a modern code editor (like VS Code), you’re ready to begin.

    In a directory of your choice, create a new Hugo site by running the following command:

    {% raw %}

    hugo new site multiauthor-blog-site
    

    Once a new Hugo project is created for you, navigate into the directory.

    cd multiauthor-blog-site
    

    Open the project in your editor and run the following command to spin a development server where you can see your changes live:

    hugo server
    

    Creating a Theme

    With your brand-new site up and running, it’s time to start customizing the canvas.

    Create a new theme inside the root directory by running hugo new theme blog-theme. This generates a folder called blog-themes inside the themes directory containing a bunch of files and folders. You’ll mostly be working with two directories: /blog-theme/layout, where all your HTML files will be present, and /blog-theme/static for static assets like images, CSS styles, and custom JavaScript.

    It’s time to tell Hugo that you’ll be using this new theme for your site. Usually, those details go inside a configurational file config.toml in the root directory. Add a key theme with a value of your theme’s name (ie, blog-theme) as shown:

    baseURL = "http://example.org/"
    languageCode = "en-us"
    title = "My New Hugo Site"
    theme = "blog-theme"
    

    Inside the baseof.html file (theme/blog-theme/layouts/default/baseof.html), add the following markup:

    <!DOCTYPE html>
    <html lang="{{ .Site.LanguageCode }}">
        {{- partial "head.html" . -}}
        <body>
            {{- partial "header.html" . -}}
            <main>
            {{- block "main" . }}{{- end }}
            </main>
            {{- partial "footer.html" . -}}
        </body>
    </html>
    

    The previous code creates some semantic elements for your site and references a site variable from your config.toml file in Hugo’s default templating language Go. If you look inside your /theme/blog-theme/layouts/partials folder, you’ll find a head.html file containing all the markup for your site’s <head> tag. Similarly, you can find a partial footer.html file to render a footer on your site.

    50 Ideas for your next blog post

    The rest of the content of your site should go inside the <main> tags.

    Add the following lines inside /theme/blog-theme/layouts/index.html to tell Hugo to insert the page content between the <main> tags in the baseof.html file:

    {{ define "main" }}
    {{ .Content }}
    {{ end }}
    

    Creating and Styling the Navbar

    The navigation bar allows users to easily visit different sections of your site.

    Inside /theme/blog-theme/layouts/partials, you can find a header.html that acts as the default header for your site. Put all the HTML for the navbar inside this file. Hugo will automatically render it on your site.

    <header>
        <nav class="navbar" role="navigation">
            <div class="navbar__left">
                <a href="/"><strong>blogsite.com</strong></a>
            </div>
            <div class="navbar__right">
                <a href="/blog">Blogs</a>
                <a href="/categories">Categories</a>
            </div>
        </nav>
    </header>
    

    The / link is to direct the user to your site’s homepage. You can leave the remaining links as it is for the moment.

    Your navbar could look better, so let’s walk through how to add custom CSS styling to your theme. The static folder inside your theme holds all the static content related to your theme, including custom styles for your templates. So all stylesheets pertaining to your theme go inside the /theme/blog-theme/static/css folder.

    Create a file called style.css and reference it inside your theme’s head.html file (/blog-theme/layouts/partials/head.html) as shown:

    <link rel="stylesheet" href="/css/style.css" type="text/css" media="all" />
    

    Hugo will push all your styles from this file onto your HTML pages.

    You can create multiple stylesheets to break down your styles into logical modules. For brevity’s sake, let’s keep all the styles for this project inside style.css only.

    body {
        background-color: #fcfcfc;
        color: #333;
        font-size: 125%;
        line-height: 1.5;
        margin: 0 auto;
        font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
      }
      .content {
        margin-bottom: 2rem;
      }
    
     .navbar{
       display: flex;
       align-items: center;
       justify-content: space-between;
       height: 25px;
       background: gainsboro;
       padding: 10px;
     }
     .navbar__right{
       display: flex;
     }
      a{
        text-decoration: none !important;
        color: black;
      }
     .navbar__right a{
       text-decoration: none;
       font-size: 14px;
       margin-right: 10px;
       color: black;
       transition: all 100ms;
     }
     .navbar__right a:hover{
       text-decoration: underline;
       font-weight: bold;
     }
     main{
       margin: 0 200px;
     }
    

    You can easily style other pages following the same pattern.

    Adding, Organizing, and Listing Blogs

    All the markdown files for your site go inside the content folder in the root directory of your project. Hugo renders the main content for your homepage from an _index.md file inside the content directory.

    Create an _index.md as shown:

    # Welcome to My Multi-Author Blog
    Hola, devs! Welcome to my site.
    

    If you visit http://localhost:1313, you can see a simple homepage for your site.

    Screenshot of the homepage

    Inside the content directory, create a blogs folder where you can organize your blog posts. You can segregate them further based on the year of publication. The following command creates a new blog post in the designated directory with some metadata:

    hugo new /blog/2020/fundamentals-design-principles.md
    

    Inside that file, add some dummy content:

    ---
    title: "Fundamentals of Design Principles"
    date: 2020-12-27T22:37:51+05:30
    draft: true
    ---
    ![](https://images.unsplash.com/photo-1516638022313-53fa45a84c7f?ixid=MXwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHw%3D&ixlib=rb-1.2.1&auto=format&fit=crop&w=1050&q=80)
    ### Diving deeper into design principles and methodologies
    Lorem...
    

    While you’re at it, add a few more blogs with some content using the same process:

    hugo new /blog/2020/java-spring-mvc.md
    hugo new /blog/2020/hugo-themes.md
    

    Hugo renders each individual blog post on the URL /blog/2020/java-spring-mvc by default. If you visit http://localhost:1313/blog/2020/java-spring-mvc, you can see the specific blog post.

    Let’s create a page for your theme that lists all the blogs and allows a user to navigate to a particular post.

    Recall that the navbar contains a route directing to /blogs, so you can render a list of all the blogs when the user visits /blogs. Hugo has a default directory where it looks for list type pages; this is where you can create a generic template for any kind of lists you want to render.

    Inside /blog-theme/_default/list.html, add the following code, which simply iterates over the .Pages variable that contains a list of all child pages for /blogs. Inside the range loop, the current context (or the dot.) is always an individual page that’s used to generate the customized link.

    {{ define "main" }}
    <div class="container">
        <div class="section">
            <div class="content">
                <h1 class="title">{{ .Title }}</h1>
                {{ .Content }}
                {{ range .Pages }}
                <ul>
                    <li><a href="{{ .Permalink }}">{{ .Title }}</a></li>
                </ul>
                {{ end }}
            </div>
        </div>
    </div>
    {{ end }}
    

    Now, if you now go to http://localhost:1313/blog, you will see a list of all your blogs. Navigate to each blog by clicking an item on the list.

    Creating a Blog Post Template

    Next, let’s create a template for your theme that renders each new blog post exactly as you’d like it to appear.

    Head over to the layouts folder (/theme/blog-theme/layouts) and create a folder named blogs with a file single.html inside it. This structure allows Hugo to render blogs faster, as it’s higher in the lookup order with respect to the single.html file present in the _default (/theme/blog-theme/layouts) directory.

    {{ define "main" }}
    <section class="section">
      <article>
        <div class="blog__container">
              <h1 class="blog__title">{{ .Title }}</h1>
                <div class="blog__details">
                  <div class="author-image-container">
                      <img src="" alt="author-image" />
                  </div>
                  <div class="blog__info">
                    <p>By  author name </p>
                    <p><time>{{ .PublishDate.Format "January 2, 2006" }}</time> |
                        {{ .ReadingTime }} {{ if eq .ReadingTime 1 }} minute {{ else }}                              minutes {{ end }} read
                     </p>
                  </div>
                  <div class="blog__categories">
                    Blog categories
                  </div>
                </div>
              <div class="content">
                {{ .Content }}
              </div>
            </div>
      </article>
    </section>
    {{ end }}
    

    The previous markup structures the contents of the blog post page by first rendering the blog’s title, followed by metadata like author name, image, an average read time, and related content categories.

    Let’s style this markup:

    .author-image{
      object-fit: cover;
      border-radius: 50%;
      width: 48px;
      height: 48px;
    }
    .blog__title{
      font-size: 32px !important;
      font-weight: bolder;
    }
    .blog__details{
      display: flex;
      align-items: center;
    }
    .blog__info{
      margin-left: 20px;
      flex: 1;
    }
    .blog__info p{
      margin-block-start: 2px;
      margin-block-end: 2px;
      font-size: 14px;
    }
    .blog__image > img{
      height: auto;
      width: 100%;
      object-fit: contain;
      margin: 10px 0;
    }
    .blog__categories{
      display: flex;
      align-items: center;
    }
    .category{
      padding: 5px 5px;
      background: #f07979;
      font-size: 12px;
      border-radius: 5px;
      width: auto;
      margin-right: 5px;
    }
    .category a{
      color: #fff;
    }
    .content img{
      height: auto;
      width: 100%;
      object-fit: contain;
      margin: 10px 0;
    }
    

    Go to /blogs and click on a blog post to see some structured content of that post. Since other details, like author information, haven’t been hooked yet, you’ll only see some boilerplate text.

    Add some hard-coded categories for your blogs inside the config.toml file:

    [params]
        categories = ["Web Development","Blogging","Web Design"]
    

    You can now simply loop over these categories and render them inside the single.html.

    Adding Support for Multiple Authors

    Now your content is there, but there’s still no information about the author.

    If you were building a single author template, you could create a variable for author name and image in the config.toml file and reference it inside the single.html file as you did for categories in the previous section. Since you want more than one author on your site, you need to add support for multiple authors in your theme.

    Inside the root folder, head over to the data directory and create a JSON file with the name of the author (for example, siddhantvarma.json) with the following information in JSON format about the author:

    {
        "name": "Siddhant Varma",
        "bio": "Howdy Guys! I'm a React and JavaScript Developer from India. I love watching             movies and creating content for developers",
        "avatar": "/img/authors/siddhantvarma.jpeg",
        "social": {
            "linkedin":"https://www.linkedin.com/in/siddhantvarma99/"
        }
    }
    

    Add more author information to your preference, like social media handles and external sites. You can use the following structure to organize your authors:

    ├── data
        └──authors
            └── siddhantvarma.json
            └── sammills.json
            └── randylev.json
    

    Create as many authors as you like. Add an author key and value pair inside the metadata for each post as shown to associate an author with a particular blog post/ Yyou must ensure that the author’s name matches exactly the name of the JSON file for that author.

    ---
    title: "Java Spring MVC"
    date: 2020-12-27T20:08:49+05:30
    author: randylev
    ---
    

    Now each blog post is associated with a JSON file describing some details about that post’s author.

    You can go back to single.html and render the author properties on the page along with the categories added in the previous section.

    {{ define "main" }}
    <section class="section">
      <article>
        <div class="blog__container">
          {{ $author := index .Site.Data.authors (.Params.author | default "default") }}
              <h1 class="blog__title">{{ .Title }}</h1>
                <div class="blog__details">
                  <div class="author-image-container">
                    <img src="{{ $author.avatar }}" class="author-image">
                  </div>
                  <div class="blog__info">
                    <p>By
                      {{- if $author -}}
                          {{ $author.name }}
                      {{- end -}}
                    </p>
                    <p><time>{{ .PublishDate.Format "January 2, 2006" }}</time> |
                        {{ .ReadingTime }} {{ if eq .ReadingTime 1 }} minute {{ else }} minutes {{ end }} read</p>
                  </div>
                  <div class="blog__categories">
                    {{ range $idx, $category := .Site.Params.categories }}
                      <div class="category"><a href="{{ "categories/" | relURL }}{{ $category | urlize }}">{{ $category }}</a></div>
                    {{- end }}
                  </div>
                </div>
              <div class="content">
                {{ .Content }}
              </div>
            </div>
      </article>
    </section>
    {{ end }}
    

    And there’s your blog with some content, metadata, an author image, and categories. You can easily navigate to other blog posts and see how various author information is rendered.

    Your blog post template

    Implementing Pagination

    Ensure good user experience on your site by allowing a user to easily paginate through the blog posts. Hugo offers this feature right out of the box by providing the variables .PrevInSection and .NextInSection. These link to the previous and next page objects to implement simple pagination for your theme.

    The following code renders two buttons at the end of each post and directs the user to the immediate previous and next post with respect to the current post.

        <div class="blog__navigate">
          <div class="blog__navigateButtons">
            <div class="blog__navigateButtons__previous">
              {{ with .PrevInSection }}
              <button><a href="{{ .Permalink }}">&#8592; Previous Post: {{ .Title }}</a>               </button>
              {{ end }}
            </div>
            <div class="blog__navigateButtons__next">
              {{ with .NextInSection }}
              <button><a href="{{ .Permalink }}">Next Post: {{ .Title }} &#8594;</a></button>
            {{ end }}
            </div>
          </div>
      </div>
    

    Let’s style this layout inside style.css:

    .blog__navigateButtons{
      display: flex;
      justify-content: space-between;
      align-items: center;
    }
    
    .blog__navigate{
      margin-bottom: 20px;
    }
    .blog__navigate button{
      border: none;
      outline: none;
      background: rgb(88, 123, 240);
      color: #fff;
      padding: 10px 20px;
      border-radius: 5px;
      cursor: pointer;
      transition: all 200ms;
    }
    .blog__navigate button:hover{
      transform: translateY(1.5);
      background: rgb(43, 92, 226);
    }
    .blog__navigate a{
      color: #fff;
    }
    

    Now you have a set of fully functional pagination buttons at the end of each post.

    Pagination buttons for a Hugo theme

    Creating a Category Page

    Categories are common on blogs for easy discovery of similar posts on your site. You already have some predefined categories inside config.toml file, so let’s create a separate category page that lists out all the categories and all the blogs associated with each one.

    You need to associate blogs with your categories, so head to your markdown files and add some categories inside the metadata.

    ---
    title: "How to create multi author blog site using Hugo"
    date: 2019-03-20T18:42:38+02:00
    description: "In this tutorial you're going to learn how to create a multi author blogging site in Hugo"
    author: siddhantvarma
    categories : ["Web Development","Web Design","Blogging"]
    ---
    

    Hugo provides taxonomy, which classifies logical relationships between content. According to Hugo’s documentation, it offers default taxonomy support for categories and tags. This means you can easily set up a category taxonomy for your site.

    Specify that you want to use the category taxonomy inside the config.toml file:

    [taxonomies]
      category = "categories"
    

    Inside your _default folder, create a file called taxonomy.html that ranges through the taxonomies, and then loops through each taxonomy to list out all the items pertaining to that taxonomy name. In this case, each taxonomy is a particular category, and blogs pertaining to each category are listed under that category as that taxonomy’s value.

    {{ define "main" }}
    <section class="section">
        <div class="container max-800px">
            <h1 class="title">
                {{ .Title }}
            </h1>
            <div class="categories">
                {{ range $taxonomyname, $taxonomy := .Site.Taxonomies }}
    
                     {{ range $name, $value := $taxonomy }}
                  <div class="categoryCard">
                     <div class="categoryHeading"><p style="font-size:large">{{ $name }} </p></div>
                          <div class="category__detail">
                            {{ range $value.Pages }}
                                <p hugo-nav="{{ .RelPermalink}}"><a href="{{ .Permalink}}"> {{ .LinkTitle }} </a> </p>
                            {{ end }}
                          </div>
                  </div>
                  {{ end }}
                {{ end }}
              </div>
        </div>
    </section>
    {{ end }}
    

    Apply some minimal styles to your template.

    .categories{
      display: flex;
      justify-content: space-between;
    }
    
    .categoryCard{
      display: flex;
      flex-direction: column;
      width: auto;
      max-width: 200px;
      min-height: 200px;
      margin-right: 5px;
      height: auto;
      background-color: #fff;
      box-shadow: rgba(149, 157, 165, 0.2) 0px 8px 24px;
    }
    
    .categoryHead{
      text-align: center;
      background-color: #f07979;
      color: #fff;
      font-weight: bold;
    }
    
    .category__details{
     padding: 0 10px;
    }
    
    .categoryCard p{
      font-size: 14px;
    }
    

    Visit /categories via the navbar to see your category page.
    Your blog’s category page

    Considering SEO

    Finally, your theme should be optimized for search engines to ensure your site draws users. Two factors in particular affect SEO for your site:

    • The SEO title
    • The meta description

    Your homepage’s title can be populated from your config.toml file. For other pages, a unique title and meta description should be dynamically generated to make these pages easily discoverable by search engines.

    Use the following generic code inside your head.html file:

    <head>
        <meta charset="utf-8">
        <meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover">
        <title>{{ if not .IsHome }}{{ with .Title }}{{ . }} | {{ end }}{{ end }}{{ .Site.Title }}</title>
        <meta name="description" content="{{ with .Description }}{{ . }}{{ else }}{{ with .Summary }}{{ . }}{{ else }}{{ .Site.Params.description }}{{end }}{{ end }}">
        <link rel="canonical" href="{{ .Permalink }}" />
        <link rel="stylesheet" href="/css/style.css" type="text/css" media="all" />
        {{ template "_internal/opengraph.html" . }}
        {{ template "_internal/twitter_cards.html" . }}
    </head>
    

    The previous code checks whether you’re at a root route, then fetches the title from the config.toml file. For other subroutes, it uses a different title according to the page you’re currently on and appends the homepage title to it.

    If you inspect each page’s <head> tag, you’ll notice a more specific and relevant title attached to it with an SEO-friendly meta description.

    {% endraw %}

    Next Steps

    Use the theme you built here as a boilerplate for your own multi-author blog. Customize it even further according to the needs of your brand—extend taxonomies by adding tags and maybe create promotional pages, like an about page or a contact page. Now that you also understand how custom styling works in Hugo, add some media queries to make your site more responsive.

    Hugo’s functionality simplifies the potentially daunting task of creating a custom theme, while offering you fine-grained control over your branding and the flexibility to accommodate change in the future.

  • Blogging Platforms for Your Startup

    Blogging Platforms for Your Startup

    There are many good reasons to start a blog for your startup: to update your customers, to generate content that ranks in search engines, or to establish your founders as subject matter experts. After you come up with some ideas, understand why you’re writing, and create a content plan, you need to decide on a blogging platform.

    How to Pick a Blogging Platform

    There are a few key factors that will influence your choice of a blogging platform. While I could devote an article to each of these, I’ll save that for another day, and just leave you with a brief summary.

    Here are the five things I consider when choosing a blogging platform:

    1. Cost to set up and maintain
    2. Ease of use
    3. Customizability
    4. Distribution
    5. Speed and scalability

    You should weigh each of these factors based on your needs and the team who will be managing the blog.

    Startup Blogging Platforms

    Setting up a blog doesn’t have to be a headache – there are many platforms out there that will help you build a professional-looking one. Below I’ve collected a few of the best options for your startup’s blog in 2021.

    Squarespace ($12) – Squarespace is my go-to recommendation for non-technical people who need a website. Squarespace works just as well for basic landing pages as it does for a full-featured website and blog.

    GraphCMS ($0) – Brings your content to any platform or static site builder. With GraphCMS, you build the essential GraphQL infrastructure and they build a headless CMS for your content creators.

    Jekyll ($0) – If you don’t mind writing a little HTML yourself, Jekyll is an awesome landing page and blogging platform. It can scale pretty much infinitely and it’s free to use with Github pages. You can also read our comparison with WordPress and our comparison with Hugo.

    Gatsby ($0) – The new, hot static site builder has a great reputation among developers, and they’re growing quickly. If your tech team uses React, you might want to consider Gatsby.

    Linkedin Publishing ($0) – Linkedin has a publishing tool that allows anyone to write blog posts that are immediately shared with their Linkedin network and profile. You don’t get any options for customizing your posts or calls to action, but it’s simple and gets automatic distribution.

    ButterCMS ($24) – If you’d prefer to use a CMS as a backend and integrate your blog into your site, an option like ButterCMS would work. This works best with a static site builder or single-page application.

    Cockpit ($0) – Cockpit is perfect if you need a flexible content structure but don’t want to be limited in how to use the content. You can use Cockpit for much more than blogging if you like.

    Contentful ($0) – Like ButterCMS, Contentful can be used as a complete content management system with an API that will hook into your site. Both solutions are only recommended for developers.

    Drupal ($0) – Free, open-source, and built on PHP, Drupal is a full-fledged content management system with a huge community. It’s more flexible than WordPress in many ways, but also requires more work to customize.

    Ghost ($0) – An open-source NodeJS blogging platform, Ghost can be installed on your server or you can use Ghost Pro to let them serve it up. In either case, Ghost provides a nice-looking theme and great options for customizing your blog.

    Medium ($0) – Medium has become really popular for startup blogs. While you don’t get to customize the look of your blog, it gets some free distribution on Medium.com.

    50 Ideas for your next blog post

    Postach.io ($9) – Allows you to use Evernote as your blog’s backend.

    Postagon ($0) – Many of the blogging platforms on this list have more features than Postagon, but if you like a clean, minimal experience, you can start writing on Postagon in less than a minute.

    Stitcher ($0) – A PHP-based static site generator. Offers the speed and flexibility of pure HTML for developers who might prefer PHP.

    Tumblr ($0) – Tumblr is simple and customizable, plus you can make it work with your own subdomain. The community features also give you some built-in distribution although it’s less popular than Medium today.

    Weebly ($0) – Weebly is a great all-in-one website and blog creator with many templates and tools to choose from.

    Wix ($0) – Wix gives you everything you need for a stunning website or blog and it’s free.

    WordPress.com ($0) – WordPress.com is a hosted blogging service. It makes setup easier, but allows for less flexibility and includes fewer plugin options than WordPress.org.

    WordPress.org ($0) – While you’ll have to install WordPress on your own server, hosting is usually cheap. The biggest downside to WordPress now is the spam that shows up because it’s so popular.

    Write.as ($0) – Quick, free (or cheap to upgrade), does custom domains, custom CSS, and it’s easy to create a bunch of blogs from one account if you upgrade.


    Need help generating high-quality content for your startup’s blog? Check out Draft.dev and let us know if we can help.