One place for hosting & domains

      Gatsby

      How To Use Static Files in Gatsby


      The author selected the Internet Archive to receive a donation as part of the Write for DOnations program.

      Introduction

      Like many popular Static Site Generators, Gatsby embraces the use of dynamic web frameworks, using React on the frontend and Node.js on the backend. But Gatsby can also pull in static files and assets, like images, CSS files, and JavaScript files.

      This tutorial covers the situations in which you might want to use static files with your Gatsby site. It will show you how to best go about adding images, stylesheets globally and as modules, JavaScript files, and arbitrary files like PDFs for your users to download.

      Prerequisites

      Before starting, here are a few things you will need:

      • A local installation of Node.js for running Gatsby and building your site. The installation procedure varies by operating system, but DigitalOcean has guides for Ubuntu 20.04 and macOS, and you can always find the latest release on the official Node.js download page.
      • A new Gatsby project, scaffolded from gatsby-starter-default. For satisfying this requirement and building a new Gatsby project from scratch, you can refer to Step 1 of the How To Set Up Your First Gatsby Website tutorial.
      • Some familiarity with React and JSX, as well as with HTML elements, if you want to customize the user interface (UI) of your posts beyond what is covered in this tutorial.
      • A program to unzip a zip archive file. On most operating systems, unzip is the command of choice, which you can download on Linux with your local package manager.
      • Access to the demo files repository used to provide sample files for this tutorial. You can access it at the DigitalOcean Community GitHub repository, and Step 1 will instruct you on how to download it.

      This tutorial was tested on Node.js v14.16.1, npm v6.14.12, Gatsby v3.13.1, and flexboxgrid v6.3.1.

      Step 1 — Preparing Example Files

      For the purposes of this tutorial, you will be working with a pre-arranged collection of static assets, which will be used throughout the following steps. The collection of files is available as a GitHub repository, and the first step of this tutorial is to download them and place them within your Gatsby project.

      First, you will extract the sample files to src/sample-assets, which you can either do manually by downloading the zip file and using an unzipping tool of your choice, or by running the following commands in your terminal at the root of your Gatsby project:

      • wget -O ../sample-assets.zip https://github.com/do-community/gatsby-static-files-tutorial-assets/archive/refs/heads/main.zip
      • unzip ../sample-assets.zip -d ./src

      The above command downloads an archive of the entire repo as a single zip archive file with wget, and then unzips the contents to the source directory.

      Once the files are unzipped, the next step is to create an empty Gatsby page component that will serve as the demo page for this tutorial. Create an empty file at src/pages/static-files-demo.js, then open the file in your editor of choice and add the following code:

      src/pages/static-files-demo.js

      import * as React from "react"
      
      import Layout from "../components/layout"
      import Seo from "../components/seo"
      
      const StaticFilesDemo = () => (
        <Layout>
          <Seo title="Static Files Demo" />
          <h1>Static Files Demo</h1>
        </Layout>
      )
      
      export default StaticFilesDemo
      

      This code serves as a Gatsby page component file that you will use to generate a public page at https://localhost/static-files-demo/. The StaticFilesDemo function is a React component that returns JSX, which becomes the page content. You use export default StaticFilesDemo as the final line, since Gatsby’s build system expects the default export of page components to be the React component responsible for rendering the page.

      After adding the page code, save the file, but keep it open as the following steps will add to it.

      In this first step you downloaded the static asset files that will be used throughout the tutorial and set up a demo page to build inside of. In the next step, you will add one of the most common forms of static assets: image files.

      Step 2 — Adding Images

      A common need for websites is to embed image files in a way that doesn’t impact the loading experience of the site. In this step, you will find out how to do this with Gatsby, using gatsby-plugin-image as well as some HTML to embed images into your Gatsby pages, while also optimizing for load time and bandwidth usage.

      Since gatsby-plugin-image is included in the gatsby-starter-default prerequisite, it is already installed as a dependency and ready for use. If you did not start your project from the gatsby-starter-default template, you can learn about installing and configuring gatsby-plugin-image in the official Gatsby docs.

      Open up the demo page component file that you created in the previous step and add the following highlighted code:

      src/pages/static-files-demo.js

      import * as React from "react"
      import { StaticImage } from "gatsby-plugin-image"
      
      import Layout from "../components/layout"
      import Seo from "../components/seo"
      
      const StaticFilesDemo = () => (
        <Layout>
          <Seo title="Static Files Demo" />
          <h1>Static Files Demo</h1>
      
          <section className="demo">
            <h2>Static Image Files Demo</h2>
      
            <figure className="image-demo">
              <StaticImage
                src="https://www.digitalocean.com/community/tutorials/gatsby-static-files-tutorial-assets-main/images/water.jpg"
                width={1000}
                quality={90}
                alt="Underwater view of clear, blue body of water"
              />
              <figcaption>
                Photo by{" "}
                <a target="_blank" rel="noreferrer noopener" href="https://unsplash.com/@cristianpalmer">
                  Cristian Palmer
                </a>
              </figcaption>
            </figure>
      
            <figure className="image-demo">
              <StaticImage
                src="https://www.digitalocean.com/community/tutorials/gatsby-static-files-tutorial-assets-main/images/turtle.jpg"
                width={1000}
                quality={90}
                alt="Overhead view of a turtle floating over blue water"
              />
              <figcaption>
                Photo by{" "}
                <a target="_blank" rel="noreferrer noopener" href="https://unsplash.com/@ruizra">
                  Randall Ruiz
                </a>
              </figcaption>
            </figure>
          </section>
        </Layout>
      )
      
      export default StaticFilesDemo
      

      Instead of using the standard HTML img tag directly and pointing it to a public image URL, in this code you are using the StaticImage component from gatsby-plugin-image and passing in the path to your local static images. This is the best practice approach, as StaticImage will generate multiple resized versions of your source images (using gatsby-plugin-sharp under the hood) and deliver the closest match to the visitor of your webpage (using the srcset feature), resulting in a faster page load and smaller download size.

      For the images passed to StaticImage, you used a quality of 90 to override the default value of 50, showing how gatsby-plugin-image can still offer improvements in filesize while preserving quality. You also specified a width of 1000, which serves as a cap on the maximum width, since both of the source images have an original width that far exceeds it. For the purposes of the demo and many web pages, 1000 pixels in width is more than enough. These two options have a substantial impact on performance, but there are many other options for gatsby-plugin-image that are worth exploring, which you can find in the Gatsby docs.

      In this page, the original versions of the two images take up roughly 4 MB combined, no matter what size screen they are viewed on. But with StaticImage, and on a small mobile phone, they will be compressed down to only 100kB, or roughly 2.5% of the original size. The loading time drops from almost 2 minutes on a 3G internet connection down to seconds. The gatsby-plugin-image plugin also takes advantage of modern image formats that are better suited to compressed web delivery, such as webp and avif.

      Save the file before moving on.

      Note: If you ever need to bypass gatsby-plugin-image and load images completely as-is, Gatsby offers a way to do this via the static folder, but this is generally advised against since this would not include the image optimizations mentioned earlier.

      You have now added several new images to your site, using best practices within Gatsby to provide an optimal user experience to visitors of your web pages. In the next step, you will focus on the styling component of web pages by adding static CSS files to your site.

      Step 3 — Adding CSS Stylesheets

      As with embedding images, there is more than one way to add CSS-based styling to a Gatsby site. Although inline styling and CSS-in-JS are always an option with Gatsby, for site-wide or component styling it is often a better practice to use dedicated static stylesheets. In this step, you will add your own custom CSS file to your Gatsby project, as well as a third-party CSS file, using an approach that follows Gatsby best practices.

      Start by creating a CSS file in the same folder as your demo page, at src/pages/static-files-demo.module.css. You are using the .module.css suffix to mark that this file is meant to be used as a CSS Module, which ensures that the styling will end up scoped to the component it is imported into and not other areas of the user interface (UI).

      After opening the newly created file, add the following code:

      src/pages/static-files-demo.module.css

      .headerText {
        width: 100%;
        text-align: center;
      }
      .container img {
        border-radius: 8px;
      }
      

      In this CSS, you are center-aligning the text in any element with the class of .headerText and making it full width, as well as giving a rounded edge to any img element inside an element with a .container class.

      Save the CSS file and close it. Now open back up the demo page component and add the following highlighted code:

      src/pages/static-files-demo.js

      import * as React from "react"
      import { StaticImage } from "gatsby-plugin-image"
      
      import Layout from "../components/layout"
      import Seo from "../components/seo"
      import * as DemoStyles from "./static-files-demo.module.css"
      
      const StaticFilesDemo = () => (
        <Layout>
          <Seo title="Static Files Demo" />
          <h1 className={DemoStyles.headerText}>Static Files Demo</h1>
      
          <section className={'demo ' + DemoStyles.container}>
            <h2>Static Image Files Demo</h2>
      
            <figure className="image-demo">
              ...
            </figure>
            <figure className="image-demo">
              ...
            </figure>
          </section>
        </Layout>
      )
      
      export default StaticFilesDemo
      

      The first change you made in this file is to add an import statement that imports the CSS file you just created. Instead of using a CSS @import statement, your are using the standard ES Module import statement, assigning the value of this import to the DemoStyles variable. Under the hood, Gatsby processes this CSS file with webpack, treating it as a CSS Module.

      You also updated the JSX in the component to use the classes from the CSS module file. You did this by updating the className attributes in strategic locations with the precise scoped class names from the DemoStyles import.

      Save your file.

      The next step involves the opposite scenario: adding static CSS code that you want to affect the entire site. An example of the kind of CSS you might want to load in this way is a small set of utility classes that help when designing flexbox-based layouts: flexboxgrid.

      Install this third-party CSS as a dependency by running this command in the root of your Gatsby project:

      Next, instead of importing the CSS that this library provides within the same demo page as before, you will import it at the highest level of your project so that it gets applied globally. In the starter template, this level is in the layout.js file, as every other component wraps its content inside it.

      Note: Another option for global CSS imports is in the gatsby-browser.js file, but Gatsby does not recommend this as the primary approach in most situations, as mentioned in the Gatsby docs about global styling. You can also use traditional <link> elements to import internal or external stylesheets, but that is also not recommended as it bypasses webpack.

      Open src/components/layout.js, and make the following change:

      src/components/layout.js

      /**
       * Layout component that queries for data
       * with Gatsby's useStaticQuery component
       *
       * See: https://www.gatsbyjs.com/docs/use-static-query/
       */
      
      import * as React from "react"
      import PropTypes from "prop-types"
      import { useStaticQuery, graphql } from "gatsby"
      
      import Header from "./header"
      import "./layout.css"
      import "../../node_modules/flexboxgrid/dist/flexboxgrid.min.css"
      
      const Layout = ({ children }) => {
        const data = useStaticQuery(graphql`
          query SiteTitleQuery {
            site {
              siteMetadata {
                title
              }
            }
          }
        `)
      
        return (
          <>
            ...
          </>
        )
      }
      
      Layout.propTypes = {
        children: PropTypes.node.isRequired,
      }
      
      export default Layout
      

      You have just added an import statement that directly imports the CSS file from the flexboxgrid library under node_modules. Since the CSS file is meant to be applied to the entire site, you are not assigning it to a variable, and because you don’t want to use it as a module, the filename does not end in .module.css.

      Save and close layout.js to complete the process of globally importing the CSS across your Gatsby site.

      With the CSS globally imported, you will now use the classes from flexboxgrid in your demo page, without having to import the CSS file again. Open the demo page component file and update the code:

      src/pages/static-files-demo.js

      import * as React from "react"
      import { StaticImage } from "gatsby-plugin-image"
      
      import Layout from "../components/layout"
      import Seo from "../components/seo"
      import * as DemoStyles from "./static-files-demo.module.css"
      
      const StaticFilesDemo = () => (
        <Layout>
          <Seo title="Static Files Demo" />
          <h1 className={DemoStyles.headerText}>Static Files Demo</h1>
      
          <section className={'demo row around-xs ' + DemoStyles.container}>
            <h2 className="col-xs-12">Static Image Files Demo</h2>
      
            <figure className="image-demo col-xs-10 col-sm-5">
              ...
            </figure>
      
            <figure className="image-demo col-xs-10 col-sm-5">
              ...
            </figure>
          </section>
        </Layout>
      )
      
      export default StaticFilesDemo
      

      You have just added some classes to your demo page that use rulesets from the flexboxgrid CSS file. The row and around-xs class on the section element turn it into a wrapped flex element with justify-content set to space-around, and the col-* classes are used to control how much of the row each element takes up.

      Make sure to save your changes to the demo file before closing it. With this change, the page has become more responsive and the images will appear side-by-side on a large enough display.

      To preview your changes so far, run this command:

      This will start a local development server for your Gatsby site at http://localhost:8000/static-files-demo. Navigate to this URL and you will find your site rendered with your new styling:

      Screenshot showing that the demo images are now side-by-side, in a row, and with a space between them

      Note: Since Gatsby ships with React out of the box, in addition to the static CSS options outlined here, you also have the option of applying styling through React and React-based frameworks.

      In this step, you used static CSS files to add additional styling to your site. In the next step, you will use a similar approach to add static JavaScript files for added functionality.

      Step 4 — Adding JavaScript Files

      Gatsby already uses JavaScript in both the backend and frontend, but this is used as either Node.js code that only runs during the build process or React components for generating the static HTML output. In this step, you will include JavaScript files that are neither Node.js- or React-based, but still get pulled into every page generated by Gatsby.

      For this tutorial, you are adding a file that prints a message to any visitor to your site that opens their developer console. You can inspect the JavaScript that will run by opening the file at src/gatsby-static-files-tutorial-assets-main/js/every-page.js.

      Rather than importing this file directly into a Gatsby file via an ES Module import (as you did with the CSS files), you will add it to the page via a Gatsby server-side rendering (SSR) API. This approach gives you fine-grained control over where in the DOM the JavaScript file is pulled in, and also prevents your code from being executed as part of Gatsby’s build process.

      However, before using the Gatsby SSR API, you need to make the static JavaScript file accessible to the public. To do this, use the special static folder that Gatsby supports by making a folder named static in the root of your Gatsby project. Then, copy the static JavaScript file to this folder. You can do both of these actions manually in your file browser, or with the following commands ran from the root of your project:

      • mkdir static
      • cp src/gatsby-static-files-tutorial-assets-main/js/every-page.js static/every-page.js

      With this action, the JavaScript file is now publicly accessible at https://localhost:8000/every-page.js. The next part of this step is to trigger it to load via HTML.

      Open up gatsby-ssr.js in the root of your Gatsby project, as that is where Gatsby will allow you to hook into the server-rendering APIs. Now add the following code:

      gatsby-ssr.js

      /**
       * Implement Gatsby's SSR (Server Side Rendering) APIs in this file.
       *
       * See: https://www.gatsbyjs.com/docs/ssr-apis/
       */
      
      import * as React from "react"
      
      export const onRenderBody = ({ setPostBodyComponents }) => {
        setPostBodyComponents([
          <script type="text/javascript" src="https://www.digitalocean.com/every-page.js" key="every-page-js" defer></script>,
        ])
      }
      

      The first line of code you added is a statement that imports React, which is necessary to enable the use of JSX in the file. Next, you export a function called onRenderBody, which takes an object as an argument with a nested function of setPostBodyComponents, which you call from within your own function. You call this function with a script tag that will load your static JS file, while using the best practices of adding a unique key property, and defer, since in this case it does not matter when the JavaScript executes.

      The setPostBodyComponents will take any React components inside the array passed as its first argument, in this case a single script tag, and render it as part of the body, thereby triggering the loading of your script file in the browser. Save the file, but keep it open for the next step.

      Now navigate to your https://localhost:8000/static-files-demo URL and open up a JavaScript console. You will find the message created by the JavaScript file, as shown in the following image:

      A browser with the demo page loaded, with the console message from `every-page.js` showing up in the JavaScript console.

      Note: If you are using the live development feature of Gatsby, you might need to halt and restart npm run develop before changes to this file take effect.

      You have now added a local static JavaScript file using the Gatsby SSR API, but the same strategy can also be used for loading external JavaScript from other domains, also known as 3rd-party scripts. To make the images in your demo easy to zoom in on, you will add a third-party lightbox library called Fancybox. In the same gatsby-ssr.js file, add the following lines:

      gatsby-ssr.js

      /**
       * Implement Gatsby's SSR (Server Side Rendering) APIs in this file.
       *
       * See: https://www.gatsbyjs.com/docs/ssr-apis/
       */
      
      import * as React from "react"
      
      export const onRenderBody = ({ setPostBodyComponents }) => {
        setPostBodyComponents([
          <script type="text/javascript" src="https://www.digitalocean.com/every-page.js" key="every-page-js" defer></script>,
          <script
            src="https://cdn.jsdelivr.net/npm/@fancyapps/[email protected]/dist/fancybox.umd.js"
            integrity="sha256-B34QrPZs5i0CQ3eqywkXHKIWw8msfAVH30RWj/i+dMo="
            crossOrigin="anonymous"
            key="fancybox-js"
            defer
          ></script>,
          <link
            rel="stylesheet"
            href="https://cdn.jsdelivr.net/npm/@fancyapps/[email protected]/dist/fancybox.css"
            integrity="sha256-WIieo0WFPkV7kcA2lQ4ZCO5gTg1Bs/SBX5YzEB4JkyM="
            crossOrigin="anonymous"
            key="fancybox-css"
          ></link>,
        ])
      }
      

      In this code, you have added both the JavaScript and CSS for Fancybox, the third-party library, through the same onRenderBody Gatsby SSR API as you are using for the local every-page.js file. Two extra attributes are used this time, both of which help with security. The crossOrigin="anonymous" explicitly tells the browser not to share credentials with a domain that does not match your own, and the integrity attribute is used to enforce Subresource Integrity (SRI), which protects against sudden changes to a file after it has been added.

      Warning: As a rule of thumb, treat third-party scripts and styles as untrusted. In addition to inspecting them before use, always use SRI. In general, loading third-party assets via URL instead of bundling with imports is something that should be avoided when possible, but is sometimes necessary for analytics, embed widgets, and error-logging services.

      This completes the task of getting the third-party code to load, but for this specific library, there is another step to trigger the new UI features.

      Save and close gatsby-ssr.js, then open src/pages/static-files-demo.js back up and make the following edits:

      src/pages/static-files-demo.js

      import * as React from "react"
      import { StaticImage } from "gatsby-plugin-image"
      
      import Layout from "../components/layout"
      import Seo from "../components/seo"
      import * as DemoStyles from "./static-files-demo.module.css"
      
      const StaticFilesDemo = () => (
        <Layout>
          <Seo title="Static Files Demo" />
          <h1 className={DemoStyles.headerText}>Static Files Demo</h1>
      
          <section className={"demo row around-xs " + DemoStyles.container}>
            <h2 className="col-xs-12">Static Image Files Demo</h2>
      
            <figure className="image-demo col-xs-10 col-sm-5">
              <StaticImage
                data-fancybox
                src="https://www.digitalocean.com/community/tutorials/gatsby-static-files-tutorial-assets-main/images/water.jpg"
                ...
              />
              <figcaption>
                ...
            </figure>
      
            <figure className="image-demo col-xs-10 col-sm-5">
              <StaticImage
                data-fancybox
                src="https://www.digitalocean.com/community/tutorials/gatsby-static-files-tutorial-assets-main/images/turtle.jpg"
                ...
      
              />
              <figcaption>
                ...
              </figcaption>
            </figure>
          </section>
        </Layout>
      )
      
      export default StaticFilesDemo
      

      Adding data-fancybox tells the fancybox library where to find images to trigger the lightbox effect on, and with that, your users can start using the lightbox viewer by clicking on a demo image, as shown in the following GIF:

      Screen recording showing that clicking a single image on the demo page launches a full-page lightbox viewer, complete with controls, which is closed after switching between the images

      Note: For loading scripts or styles in the <head> of the page, both your own and third-party, the recommended approach is with the gatsby-plugin-react-helmet plugin. This plugin is bundled with the starter template, and you can add to an existing usage of it in src/components/seo.js.

      You just used two different ways to pull local and remote JavaScript into your Gatsby site, each with its own use cases and trade-offs. For file types not covered by this step or previous ones, the next section in this tutorial will address how to include arbitrary files in your Gatsby site.

      Step 5 — Adding Arbitrary Static Files

      You have now implemented three common types of static assets in web development: images, CSS, and JavaScript. But that still leaves lots of other file types that might be part of a website. In this step, you will explore adding arbitrary static files to your Gatsby site, so that they can be embedded or offered as downloads to your visitors.

      The first way to add arbitrary static files so that users can access them is to import them inside of JavaScript files and use webpack to generate a public link for you. This strategy is recommended by the official Gatsby docs on importing assets, since webpack will help prevent typos in paths, avoid unnecessary disk space usage for files that are never imported, and in some cases, even inline the contents of the file as a data URI.

      Open the demo page component file and add the following edit:

      src/pages/static-files-demo.js

      import * as React from "react"
      import { StaticImage } from "gatsby-plugin-image"
      
      import Layout from "../components/layout"
      import Seo from "../components/seo"
      import * as DemoStyles from "./static-files-demo.module.css"
      import helloWorldPdf from "../gatsby-static-files-tutorial-assets-main/hello-world.pdf"
      
      const StaticFilesDemo = () => (
        <Layout>
          <Seo title="Static Files Demo" />
          <h1 className={DemoStyles.headerText}>Static Files Demo</h1>
      
          <section>
            <h2>Arbitrary Static Files</h2>
            <a href={helloWorldPdf} title="Download the Hello World file">
              Access the Hello World file by clicking here.
            </a>
          </section>
      
          <section className={"demo row around-xs " + DemoStyles.container}>
            <h2 className="col-xs-12">Static Image Files Demo</h2>
            ...
          </section>
        </Layout>
      )
      
      export default StaticFilesDemo
      

      In this code, you are importing the static file (a PDF file) directly in JavaScript, which will get parsed by webpack and will generate a unique link. That link will populate as the value of helloWorldPdf, which is what your a tag is pointing to.

      Due to the way assets are handled with this method, the final link will look rather random, like /static/hello-world-2f669160afa9b953cbe496f2d6ccb046.pdf. This works for most scenarios, but if you need a permanent readable link, Gatsby offers another option in the form of the special static folder. Pretending that you are an employer that wants your employees to be able to bookmark the link of your_domain.com/time-off-form.pdf, you will add a new file using this static folder.

      First, copy the static file time-off-form.pdf from the demo files directory to the root static folder. You can do this manually, or with the following command:

      • cp src/gatsby-static-files-tutorial-assets-main/time-off-form.pdf static/time-off-form.pdf

      Next, add a link to it in the page file:

      src/pages/static-files-demo.js

      import * as React from "react"
      
      ...
      
      import helloWorldPdf from "../gatsby-static-files-tutorial-assets-main/hello-world.pdf"
      
      const StaticFilesDemo = () => (
        <Layout>
          <Seo title="Static Files Demo" />
          <h1 className={DemoStyles.headerText}>Static Files Demo</h1>
      
          <section>
            <h2>Arbitrary Static Files</h2>
            <a href={helloWorldPdf} title="Download the Hello World file">
              Access the Hello World file by clicking here.
            </a>
            <br />
            <a href="https://www.digitalocean.com/time-off-form.pdf" title="Time Off Form">
              Request Time Off - Form to fill out and submit.
            </a>
          </section>
      
          <section className={"demo row around-xs " + DemoStyles.container}>
            ...
          </section>
        </Layout>
      )
      
      export default StaticFilesDemo
      

      Save the changes to this file and close it.

      With the static folder approach, you get to provide your users with a permanent path of /time-off-form.pdf, but you lose the benefits of cache-busting for file changes and having webpack’s compilation step catching typos in filepaths.

      Navigate to https://localhost:8000/time-off-form.pdf to view the following PDF:

      A sample PDF that has instructions for employees to log their time off.

      Thanks to your efforts in this step, visitors to your site can now access extra static file types enabled by your new code. This is convenient for your site visitors, as they don’t have to leave your domain to access these files, and beneficial for you, as it makes them less likely to leave the site entirely.

      Note: If you are using Markdown as a source within Gatsby and want to automatically copy files that are linked inside Markdown links to the public part of your site so that users can download them, please take a look at the gatsby-remark-copy-linked-files plugin.

      Conclusion

      Through following the steps in this tutorial, you have added new types of static files and assets to your website, integrating them into the Gatsby system and your existing content. These approaches can be applied to almost any new or existing Gatsby site and across hundreds of use-cases and file types, so you are now well-equipped to handle static files in Gatsby for the future.

      Static files are involved with many parts of web development, and impact or are impacted by related design decisions. Some topics that are worth further consideration are:

      If you would like to read more on Gatsby, check out the rest of the How To Create Static Web Sites with Gatsby.js series.



      Source link

      How To Use Themes in Gatsby


      The author selected the Internet Archive to receive a donation as part of the Write for DOnations program.

      Introduction

      Gatsby 1.0 was released in 2017, and since then has continued to release new features, while maintaining its high customizability as a static site generator. Added features have taken the form of plugins, new APIs, utilities, and configuration options. Each of these features can be individually used and customized or combined together to apply specific use cases. However, since many sites benefit from the same combinations of features and settings, Gatsby introduced Gatsby themes.

      Themes in Gatsby refer specifically to plugins that act as a collection of configuration options, functionality, and/or user interface (UI) elements. Separating out shared features into maintained themes makes keeping your site up-to-date easier, and also lets you spend less time configuring your site and more time developing your content.

      In this tutorial, you will install, configure, and use a Gatsby theme for publishing blog posts: gatsby-theme-blog. This plugin bundles multiple features, such as MDX support and code highlighting, into one convenient package. Throughout the tutorial, you will follow the process of using this specific Gatsby theme, which you can then apply to themes in general.

      Each step will take you through an important part of using Gatsby themes, and by the end you will have seen how this process can be applied to any Gatsby theme that you might want to use in the future.

      Prerequisites

      Before starting, here are a few things you will need:

      • A local installation of Node.js for running Gatsby and building your site. The installation procedure varies by operating system, but DigitalOcean has guides for Ubuntu 20.04 and macOS, and you can always find the latest release on the official Node.js download page.
      • Some familiarity with JavaScript, for working in Gatsby. The JavaScript language is an expansive topic, but a good starting point is our How To Code in JavaScript series.
      • A new Gatsby project, scaffolded from gatsby-starter-default. To build a new Gatsby project from scratch, you can refer to Step 1 of the How To Set Up Your First Gatsby Website tutorial.

      This tutorial was tested on Node.js v14.16.1, npm v6.14.12, Gatsby v3.11.1, and gatsby-theme-blog v3.0.0.

      Step 1 — Finding and Installing a Theme

      Although this tutorial will walk you through using a specific theme, gatsby-theme-blog, each step also applies conceptually to Gatsby themes in general. This applies to this first step as well: finding a theme you wish to use and installing it with npm.

      Gatsby provides guidance to plugin and theme authors on how they should publish their Gatsby packages, which makes it easier to find a theme to fit your needs. Theme developers are instructed to tag their theme packages with both gatsby and gatsby-theme as keywords, which are then scanned by package registries (where the files are actually hosted) and made searchable.

      This tutorial follows the use-case of building a Gatsby-powered site with a blog sub-section. As the developer, you are looking for a theme to add support for MDX, code highlighting, and more. Although Gatsby has its own plugin browser, it actually pulls its listings from the npm registry, so your first step is to start your search directly in the npm registry search engine. By using the search input of blog keywords:gatsby-theme, you will limit your results to only those plugins that have the gatsby-theme keyword, as shown in the following screenshot:

      npm results page for the

      In this tutorial, you will be using gatsby-theme-blog, so select that package. With gatsby-theme-blog selected as the theme you are going to install, the next part of this step is to actually install it, along with its dependencies. Navigate to your existing Gatsby project and run the following command in the directory:

      • npm install gatsby-theme-blog

      After the dependencies have finished installing, you will see a message similar to the following:

      Output

      ... + [email protected] added 262 packages from 181 contributors and audited 2391 packages in 49.706s

      Now that you have installed the theme and its dependencies into your project, it is time to move on to loading and configuring the theme within your Gatsby project.

      Step 2 — Loading and Configuring the Theme

      Now that your theme is installed, you can start to actually use it across your site and modify it to your needs. In Gatsby, the main theme initialization and configuration is done by editing the root configuration file, gatsby-config.js. In this step, you will edit the configuration file to load in your theme with your desired options.

      Open the gatsby-config.js configuration file in your preferred editor, then add the following:

      gatsby-config.js

      module.exports = {
        plugins: [
          ...
          `gatsby-plugin-image`,
          {
            resolve: 'gatsby-theme-blog',
            options: {
              basePath: '/posts',
              contentPath: `md/posts`,
            }
          },
          `gatsby-transformer-sharp`,
          ...
        ]
      }
      

      In this configuration code, there are two important settings that you are using custom values for. The theme uses the basePath option to set the blog’s URL, and contentPath tells the theme where to find the Markdown files to publish as blog posts. Using a value of md/posts for contentPath means that your Markdown files must reside in the md/posts directory.

      Once you have added this code, save your configuration file.

      The gatsby-theme-blog theme offers additional settings, documented in the gatsby-theme-blog README file. Since each Gatsby theme is different, the most important part of this step is referring to the documentation of your selected theme and following the exact guidance provided in it.

      You have now set up the theme to be loaded and configured to your liking through the Gatsby configuration file. The next step is to explore some of the new functionality that it adds to your site, testing along the way.

      Step 3 — Testing Functionality

      As your theme is now installed, configured, and loaded, you can now implement it in your site. This step covers how to try out some of the new functionality bundled with the theme and preview your results.

      You will test out the MDX, code highlighting, and Markdown processing support of gatsby-theme-blog with a new blog post file. First, you will need to make the directory to hold the files, which needs to correspond with the contentPath setting of md/posts you used in step #2. You can either make this directory manually in your file browser or make it in your terminal by running this command in the root of your Gatsby project:

      Next, create an empty MDX file, my-first-post.mdx, that will contain your new post content. Again, you can create this manually or in the terminal:

      • touch ./md/posts/my-first-post.mdx

      Now open up the empty MDX file and add in the following code:

      md/posts/my-first-post.mdx

      ---
      title: Learning Gatsby Themes and Trying MDX
      slug: /posts/gatsby-theme-learning
      date: 2021-08-16
      excerpt: A post about learning Gatsby themes and trying out some MDX.
      ---
      
      ## Welcome!
      
      This is a post where I plan to share my journey learning Gatsby Themes, and to try out some MDX.
      
      ## Resources
      
      <ul>
      {[
          {
              link: 'https://www.gatsbyjs.com/',
              text: 'Gatsby Website',
              note: 'Official Website for Gatsby'
          },
          {
              link: 'https://www.gatsbyjs.com/docs/themes/',
              text: 'Gatsby Theme Documentation',
              note: 'Documentation for Gatsby Theme usage and development'
          },
          {
              link: 'https://www.digitalocean.com/community/tutorial_series/how-to-create-static-web-sites-with-gatsby-js',
              text: 'DigitalOcean - "How To Create Static Web Sites with Gatsby.js"',
              note: 'A DigitalOcean tutorial series on using Gatsby JS'
          }
      ].map(item => (
          <li key={item.link}>
              <a href={item.link} target="_blank">{item.text}</a>
              <ul>
                  <li>{item.note}</li>
              </ul>
          </li>
      ))}
      </ul>
      
      ## Code Sample
      
      To try out code highlighting in this theme, here is a snippet of JavaScript code. This code won't run in your browser; it is for visual use only.
      

      At the top of the file, the section enclosed by --- is a set of key-value pairs called frontmatter. Not every theme uses the same keys, and the ones you are using in your post have been carefully selected out of the keys used by gatsby-theme-blog. You have defined a custom title, slug (the URL path), publication date, and excerpt (preview text to display on the /posts listing page).

      All the text that follows the frontmatter becomes the body of the post, starting with your Welcome! section. The two hash symbols (##) before the heading text tell Markdown this is a level-2 heading, which is used for the Resources section as well.

      In the Resources section, you have your first usage of what makes MDX different from regular Markdown: the use of React’s JSX syntax to embed React components that get merged with your Markdown and rendered into a single page. In your post, you are using JSX to turn a collection of resources about Gatsby into an HTML list of links.

      Finally, to test out the code syntax highlighting feature bundled with gatsby-theme-blog, add a Markdown Fenced Code Block at the end of the file:

      md/posts/my-first-post.mdx

          ```js
          function saySomething(name) {
              console.log(`Hello ${name}!`);
              console.log(`Isn't learning about Gatsby fun?!`);
          }
          saySomething('Arthur');
          ```
      

      This uses triple backticks to indicate the boundaries of where the code starts and stops.

      Save and close the MDX file, as you are done editing the new post.

      To see this post as a viewer would, testing all the features of your theme plugin, run the following command:

      Once ready, the Gatsby CLI will prompt you to open your project in your web browser, which you can do by navigating to localhost:8000. To see the new blog listing page, visit localhost:8000/posts, and to see this specific new post, navigate to localhost:8000/posts/gatsby-theme-learning/. The blog post will look like the following:

      The tutorial's MDX blog post, built with Gatsby and rendered in the browser.

      You have just tested out some of the functionality that your newly installed theme provides, and viewed the results of your efforts in a web browser. For many users, this might cover all their needs, but for a higher level of customization, the next step explores a Gatsby concept called shadowing that lets you override pieces of a theme.

      Step 4 — Using Shadowing (Optional)

      At this point in the tutorial, you have already installed and configured a third-party theme within Gatsby. Configuration took place in gatsby-config.js, and was limited to the options that the theme publisher chose to make customizable. If you needed to customize a theme beyond these options, you would use a Gatsby concept called shadowing, which you will do in this step.

      The term shadowing refers to the practice of overriding or extending a built-in theme file with your own modifications. For those familiar with WordPress, this is similar to the concept of a child theme.

      With Gatsby themes, any file in the theme’s source code can be shadowed, from methods that affect Gatsby nodes and file generation to UI elements and layouts. For your blog, you will shadow a React component file named bio-content.js to customize how your blog bio appears below each post. By shadowing this one file, you will affect the appearance of every blog post that goes through the gatsby-theme-blog plugin.

      The first step of shadowing is to create a folder in your src directory with the same exact name as the theme plugin you want to shadow. You can do this manually, or with the terminal:

      • mkdir src/gatsby-theme-blog

      For any particular file in the theme that you want to shadow, the next step is to create a file with that exact same name and in the same directory structure as in the theme. Because you are only going to slightly modify the existing bio component, you can save some time by copying the existing file over as a starting point, with the cp (copy) command:

      • mkdir -p src/gatsby-theme-blog/components
      • cp node_modules/gatsby-theme-blog/src/components/bio-content.js src/gatsby-theme-blog/components/bio-content.js

      Now open the newly copied file, and make the following modifications:

      src/gatsby-theme-blog/components/bio-content.js

      import React, { Fragment } from "react"
      
      const BioContent = () => (
        <Fragment>
          <p>Content by DigitalOcean</p>
          <p>License Info:</p>
          <p
            style={{
              margin: "10px 20px",
              padding: 8,
              backgroundColor: "#0069ff",
              color: "white",
              borderRadius: 12,
            }}
          >
            This work is licensed under a Creative Commons
            Attribution-NonCommercial-ShareAlike 4.0 International License.
          </p>
        </Fragment>
      )
      
      export default BioContent
      

      Save and close this file, as you are now done editing the shadow file.

      In this file, you have shadowed the original bio-content.js file, replacing the placeholder text with an author name and license information. You have done so by replacing the JSX returned by the BioContent React component. The style={{}} code is an example of inline CSS, which you have used to add some color and spacing to the license callout.

      By running npm run develop again in your terminal, you will launch the Gatsby development server and can preview the changes across all your blog posts:

      Screenshot showing the new bio-content that lists

      By using Gatsby shadowing, you have just modified a third-party Gatsby theme beyond the standard configuration, mixing in your own custom overrides and extensions.

      Conclusion

      By following the steps in this tutorial, you now have a Gatsby site that uses a published theme to pull in multiple components and pieces of bundled functionality as a single dependency. Thanks to this packaged functionality, your site is now easier to update and can contain much less configuration and setup code, despite having additional features. You have also learned how to use shadowing to customize Gatsby themes at a deeper file-based level, unlocking another layer of extensibility.

      Although this tutorial covers a specific theme, the concepts and approaches it outlines apply to Gatsby themes in general. For more on Gatsby themes, take a look at the official Gatsby documentation. If you would like to read more about Gatsby, check out the rest of the How To Create Static Web Sites with Gatsby.js series.



      Source link

      How To Create a Custom Source Plugin in Gatsby


      The author selected the Internet Archive to receive a donation as part of the Write for DOnations program.

      Introduction

      In building a website or application, often one of the most difficult tasks is pulling data from multiple sources and collating it into a uniform output. A common way to solve this is to use entirely different build systems for different parts of a site, but this sometimes adds complexity while making uniformity harder to achieve. This is where Gatsby, a data-driven Static Site Generator (SSG), can provide a solution.

      One of the core objectives of Gatsby is to solve this problem for developers, and source plugins are the main way it does so. A source plugin is a bundle of code that handles bringing data into the Gatsby ecosystem from a given source. Sources can be from the local filesystem as with Markdown files, databases, published data feeds, or even completely dynamic remote data sources such as APIs.

      In this tutorial, you will build your own custom source plugin to bring new data into Gatsby from a real-world API. You will also format the data so that it can be accessed throughout Gatsby, and by the end of the tutorial, have a working project that builds static HTML from your new dynamic data source.

      Prerequisites

      Before starting, here are a few things you will need:

      • A local installation of Node.js for running Gatsby and building your site. The installation procedure varies by operating system, but DigitalOcean has guides for Ubuntu 20.04 and macOS, and you can always find the latest release on the official Node.js download page.
      • Some familiarity with JavaScript, for working in Gatsby. The JavaScript language is an expansive topic, but a good starting spot is our How To Code in JavaScript series.
      • Some familiarity with web APIs, Node.js, and JSON.
      • A new Gatsby project, scaffolded from gatsby-starter-default. For satisfying this requirement and building a new Gatsby project from scratch, you can refer to Step 1 of the How To Set Up Your First Gatsby Website tutorial.
      • Some familiarity with React and JSX, as well as HTML elements, if you want to customize the user interface (UI) of your posts beyond what is covered in this tutorial.

      This tutorial was tested on Node.js v14.16.1, npm v6.14.12, Gatsby v3.13.0, and node-fetch v2.6.2.

      Step 1 — Scaffolding Files and Installing Dependencies

      When building something, the first step is always to get your tools and parts in order. In this step, you will put the initial building blocks of your source plugin in place by creating the necessary file structure and installing the dependencies your code will rely on.

      Since this will be a local plugin, create a directory within your Gatsby project to hold the source code of the plugin under a root-level plugins directory. You can do this by manually creating the folder in your file browser, or from the command line in the root of your project with the mkdir command:

      • mkdir -p plugins/my-custom-source-plugin

      Note: If you want to develop your plugin outside of your Gatsby project directory, you can do so, but it requires some extra steps to get Gatsby to pull the files into your site. For more on this, check out the official Gatsby documentation.

      Next, you need to create a package.json file to mark this directory as a Node.js package with its own dependencies. To create this file and pre-fill some required fields, use the following command:

      • cd plugins/my-custom-source-plugin
      • npm init -y

      This command navigates to your newly created plugin folder and then uses npm init to initialize a new package. The -y flag skips some questions that are irrelevant to this project and fills in the package.json file with the minimum required values.

      Now that package.json exists, you can add dependencies to your plugin that will make coding out functionality easier. Go ahead and install the only extra dependency you will be needing in this tutorial, node-fetch, by using the following command:

      • npm install node-fetch@^2

      Finally, create the gatsby-node.js file that will end up holding the main code of the source plugin:

      Note: If you are frequently building Gatsby plugins, you might find their plugin template helpful.

      Now that you have created the file structure to support your plugin and installed the initial dependencies, you will move on to giving instructions to Gatsby on how to find and load your plugin.

      Step 2 — Loading and Configuring the Plugin

      As is the case with any Gatsby plugin or theme, Gatsby has to be instructed on where and how to load the plugin from. To do this, you edit the main Gatsby config file gatsby-config.js , which resides in the root of your Gatsby project. Open the file in your editor of choice and add the following highlighted line:

      gatsby-config.js

      module.exports = {
      ...
        plugins: [
          `gatsby-plugin-react-helmet`,
          `gatsby-plugin-image`,
          {
            resolve: `gatsby-source-filesystem`,
            options: {
              name: `images`,
              path: `${__dirname}/src/images`,
            },
          },
          `my-custom-source-plugin`,
          `gatsby-transformer-sharp`,
      ...
      

      Because your plugin’s source code lives in the plugins directory, all that is required to get Gatsby to load it is to pass in the name of the sub-directory where it can be found within that folder. Your plugin also does not take any options at the moment, so there is no need to pass an options object to it in the Gatsby configuration block.

      Save gatsby-config.js and exit from the file.

      You have now configured Gatsby to load your custom source plugin, as well as told it exactly where to find the source code it should execute. In the next step, you will build out this source code to pull data into the Node.js runtime from your custom remote source.

      Step 3 — Pulling Raw Data Into Node.js

      In the previous step, you configured Gatsby to load and execute your custom source plugin’s code, but you still need to build out this code to accomplish the task of bringing new data into the Gatsby ecosystem. In this step, you will write the code that does this, fetching remote data via node-fetch and preparing it for use in future steps.

      Source plugins can pull data from almost anywhere, local or remote, but in this tutorial your source plugin will be specifically pulling titles and excerpts from the Computer Programming Books category in Wikipedia via their public API.

      Open your my-custom-source-plugin/gatsby-node.js file in your plugins directory and add the following code:

      plugins/my-custom-source-plugin/gatsby-node.js

      const fetch = require('node-fetch').default
      
      /**
       * Fetch a list of computer books from Wikipedia, with excerpts
       */
      async function getWikiProgrammingBooks() {
        const BASE_ENDPOINT = "https://en.wikipedia.org/w/api.php?action=query&format=json&utf8=1&redirects=1";
      
        // Get list of books
        const listEndpoint = new URL(BASE_ENDPOINT);
        listEndpoint.searchParams.append('list', 'categorymembers');
        listEndpoint.searchParams.append("cmtitle", "Category:Computer_programming_books");
        listEndpoint.searchParams.append("cmlimit", "10");
        const listResults = await (await fetch(listEndpoint.toString())).json();
      
      
        // Extract out the page IDs from the list
        const pageIds = listResults.query.categorymembers.map((listing) => listing.pageid);
      
        // Fetch details for page IDs
        const extractEndpoint = new URL(BASE_ENDPOINT);
        extractEndpoint.searchParams.append("pageids", pageIds.join("|"));
        extractEndpoint.searchParams.append("prop", "extracts|info");
        extractEndpoint.searchParams.append("exintro", "");
        extractEndpoint.searchParams.append("explaintext", "");
        extractEndpoint.searchParams.append("inprop", "url");
      
        const bookResult = await (await fetch(extractEndpoint.toString())).json();
      
        return Object.values(bookResult.query.pages);
      }
      

      In this code, you have created a reusable function that can be called to return a list of computer programming books, along with their page IDs (a unique ID within Wikipedia) and excerpts/extracts. In the first part of the function, you build up the right API URL to use to fetch the initial list of titles and IDs belonging to a specific category (Computer Programming Books). The URL constructor and interface is used to make modifying the query string more readable and manageable.

      You use the fetch method from node-fetch to make a GET request to the constructed URL, which returns a list of the book titles with their IDs. That response is then turned into an array of just the pageid values, subsequently used to query the Wikipedia API again, this time requesting extracts and meta info generated for the given page ID. The page IDs are joined by the pipe character (|), as the Wikipedia API uses this format to accept multiple IDs through a single string value.

      Finally, since the results for page excerpts come back as an object with each book listing nested under its own ID as a key, you use Object.values() to omit the page ID key and convert the results into an array before returning them.

      If you were to log the output of this function, it would look something like this:

      [
        {
          "pageid": 379671,
          "ns": 0,
          "title": "The C Programming Language",
          "extract": "The C Programming Language (sometimes termed K&R, after its authors' initials) is a computer programming book written by Brian Kernighan and Dennis Ritchie...",
          "fullurl": "https://en.wikipedia.org/wiki/The_C_Programming_Language",
          ...
        },
        ...
      ]
      

      Make sure to save your changes, but keep this file open as you will be adding more code to it in the next step.

      In this step you used node-fetch to retrieve remote source content and expose it within the gatsby-node.js file. In the next step, you will normalize the content as you use it to create new Gatsby nodes to use throughout the Gatsby project.

      Step 4 — Normalizing Data and Creating Nodes

      Fetching remote content and bringing it into gatsby-node.js in the previous step doesn’t mean that it is now accessible throughout Gatsby; in order to share data in a universal way, Gatsby uses the concept of nodes, which are shared across a unified GraphQL data layer. In this step, you will create these nodes, formatting your new content to match.

      Although you can now retrieve and access the results from Wikipedia by calling getWikiProgrammingBooks(), you still need to add the code to integrate this with Gatsby’s node system. In the same gatsby-node.js file from the previous step, add this new block of code to handle generating the nodes:

      plugins/my-custom-source-plugin/gatsby-node.js

      const fetch = require('node-fetch').default
      
      ...
      
      exports.sourceNodes = async ({ actions, createContentDigest, createNodeId }) => {
        // Arbitrary node type constant
        const BOOK_TYPE = 'BookWikiPage';
      
        // Get books
        const bookResults = await getWikiProgrammingBooks();
      
        // Convert raw book results to nodes
        for (const book of bookResults) {
          actions.createNode({
            ...book,
            id: createNodeId(`${BOOK_TYPE}-${book.pageid}`),
            parent: null,
            children: [],
            internal: {
              type: BOOK_TYPE,
              contentDigest: createContentDigest(book)
            }
          })
        }
      };
      

      In this code block, you are iterating over each book returned by getWikiProgrammingBooks and creating a Gatsby node for it via the createNode method. Each property and value passed into createNode has importance, and is worth consideration:

      • ...book is used to spread the key-value pairs from your Wikipedia API object into the Gatsby node you are creating. This means that later on you can access node.title, as it will be copied from book.title.
      • id is a globally unique value within Gatsby. To make each book’s ID unique within your own plugin, you are combining the book type with the Wikipedia page ID to form an ID string. However, because you can’t be sure what IDs other plugins are using, you’ve used the best practice of passing your ID to createNodeId, which is a Gatsby helper function that ensures the ID is turned into something globally unique.
      • parent is a field that can be used to link your node to another via ID, marking this node as a child. Since each book is its own entity, unconnected to other nodes, you have left this as null, signifying it does not have a parent.
      • children is similar to parent as a way to link nodes, but takes an array of IDs. Since each book has no children, you have left the array empty.
      • internal is an object that groups together fields highly-specific to Gatsby’s internal node management system and other plugins. It can only contain official fields, which is why you did not spread the book object into it.
      • type is a globally unique string that describes the type of node you are creating, and will be used later when querying for nodes via GraphQL.
      • contentDigestis a hash string, which is built from the contents of the node and the Gatsby createContentDigest helper utility. This field helps Gatsby detect when a node has changed, as the hash string will change if any properties of the book object are modified.

      You have just added code that takes your source content and creates new Gatsby nodes with it, sharing them across the Gatsby environment. In the next step, you will verify that these nodes appear in the GraphQL data layer and can be queried.

      Step 5 — (Optional) Inspecting Node Output with the GraphQL API

      By now, you have pulled your source content into Gatsby and used it to create new nodes. As an alternative to manually debugging with breakpoints or log statements, in this step you will use the interactive GraphQL IDE to verify that these new nodes are being created and are able to be queried with the GraphQL API.

      Go ahead and start up your local development server by running this command from the root of your Gatsby project:

      Note: At the time of writing this tutorial, a problem with Gatsby’s dependency chain introduced an error that can return the message Error: Cannot find module 'gatsby-core-utils' when attempting to start the development server. If you encounter this error, run the following:

      • npm install gatsby-core-utils

      This will re-install the Gatsby core utilities and will resolve the dependency problem. For more information on this, check out the GitHub issue for this Gatsby error.

      In addition to launching a live version of your Gatsby site, the develop command also exposes a local GraphQL server and IDE. To verify that your code in gatsby-node.js is creating all the book nodes, you will use this GraphQL query to fetch the book titles, page IDs, and Gatsby IDs:

      {
        allBookWikiPage {
          edges {
            node {
              title
              pageid
              id
            }
          }
        }
      }
      

      To run this query, either open up the interactive GraphQL IDE at localhost:8000/___graphql and paste the query into the left side before executing, or query it via cURL:

      • curl --location --request POST 'http://localhost:8000/___graphql'
      • --header 'Content-Type: application/json'
      • --data-raw '{
      • "query": "{ allBookWikiPage { edges { node { title pageid id } } } }"
      • }'

      The response JSON will look something like this:

      {
        "data": {
          "allBookWikiPage": {
            "edges": [
              {
                "node": {
                  "title": "The C Programming Language",
                  "pageid": 379671,
                  "id": "818771ca-40aa-5cfd-b9e7-fddff093d5ec"
                }
              },
              ...
            ]
          }
        },
        "extensions": {}
      }
      

      Having verified that your new custom source nodes have been created and are accessible in the GraphQL data layer, the next step is to use them to create visible content for visitors of your site or application.

      Step 6 — (Optional) Creating Pages Based on Nodes

      So far, all of the previous steps have been focused on creating internal Gatsby nodes, including the last step of verifying their creation and ability to be retrieved. However, these nodes are only visible to code running in your Gatsby project, not to visitors of your site or application. In this step, you will add a React page template file and wire it up to your nodes so that your source plugin content turns into actual public-facing webpages.

      There are multiple ways to create pages based on Gatsby nodes, but for this tutorial you will be using the File System Route API, which creates pages based on a special filename syntax.

      First, create an empty file in src/pages with a filename of {BookWikiPage.title}.js. The curly braces tell Gatsby that the filename is using the File System Route API, and inside the braces, BookWikiPage.title tells Gatsby to create a page for each unique book title. Note that you are no longer working on files within the plugins directory, but are now working inside of the main Gatsby project.

      Next, add code to that file that will take the book node and display it as a webpage:

      src/pages/{BookWikiPage.title}.js

      import { graphql } from "gatsby";
      import * as React from "react";
      import Layout from "../components/layout";
      import Seo from "../components/seo";
      
      export default function BookPageTemplate({ data: { bookWikiPage } }) {
        const { title, extract, fullurl } = bookWikiPage;
        return (
          <Layout>
            <Seo title={title} />
            <h1>{title}</h1>
            <blockquote>{extract}</blockquote>
      
            <i>This article uses material from the Wikipedia article <a href={fullurl} target="_blank" rel="noreferrer">"{title}"</a>, which is released under the <a href="https://creativecommons.org/licenses/by-sa/3.0/">Creative Commons Attribution-Share-Alike License 3.0</a>.</i>
          </Layout>
        );
      }
      
      export const pageQuery = graphql`
        query ($id: String!) {
          bookWikiPage(id: { eq: $id }) {
            title
            extract
            fullurl
          }
        }
      `;
      

      At the end of your code is an exported variable called pageQuery, which uses the Gatsby GraphQL tag. Gatsby will evaluate the GraphQL query that follows it, passing the results to the BookPageTemplate function.

      The BookPageTemplate function, which is a React component, then takes the results of the GraphQL query and displays them as part of a web page by embedding the values into the JSX that it returns. The title of the book is used as the main heading and title of the page, the extract is displayed as a block quote, and a link to the full Wikipedia entry page is embedded at the bottom.

      You also mark the BookPageTemplate function as the default export by using export default before its declaration, as Gatsby expects to find the React component responsible for producing the final rendered page as the default export of each page template file.

      Having added the React template code to the file, save the changes and close it. Navigate to http://localhost:8000/the-c-programming-language/ to render a sample page:

      Screenshot showing a book listing page generated for

      Note: For a more manual approach to creating pages based on nodes, you can use the createPages API inside of gatsby-node.js.

      To display a listing of these new nodes and their associated pages, you will also create a dedicated listing page, which will display all the books in one location. Under src/pages, create a new file with the filename of books.js. Next, add the following code into it:

      src/pages/books.js

      import { graphql, Link } from "gatsby";
      import * as React from "react";
      import Layout from "../components/layout";
      import Seo from "../components/seo";
      
      export default function BookListingsPageTemplate({ data: { allBookWikiPage } }) {
        return (
          <Layout>
            <Seo title="Programming Books Listing" />
            <p>Here are some computer programming books that have their own Wikipedia entries:</p>
      
            {allBookWikiPage.edges.map((edge) => {
              const node = edge.node;
              return (
                <details key={node.title}>
                  <summary>{node.title}</summary>
      
                  <div className="details-body">
                    <p>{node.extract}</p>
                    <div className="links">
                      <Link href={node.gatsbyPath}>Internal Page</Link>
                      <a rel="noreferrer" href={node.fullurl}>Wikipedia Page</a>
                    </div>
                  </div>
                </details>
              )
            })}
          </Layout>
        );
      }
      
      export const pageQuery = graphql`
        query {
          allBookWikiPage {
            edges {
              node {
                title
                extract
                gatsbyPath(filePath: "/{BookWikiPage.title}")
                fullurl
              }
            }
          }
        }
      `;
      

      Similar to the {BookWikiPage.title}.js page template, this file also uses the GraphQL pageQuery tag to pull in data from the GraphQL layer and pass it to a React component. However, while the previous template rendered a single book based on ID, this template will render a listing of all the books, while linking to the individual book pages created previously.

      Each book listing uses a <details> element, which enables the listing to expand to show the full excerpt of the book and links or to collapse to show just the title. Following best practices, you also pass a unique value to key while iterating through the array, using the Gatsby Link component for internal links and a tags for external links.

      The gatsbyPath(filePath: "/{BookWikiPage.title}") string in the GraphQL query uses the special gatsbyPath() function to retrieve the public path that will be created based on the File System Route API filename that is passed in.

      Save and exit from this file.

      Note: When changing data sources for components, the hot re-loading feature will sometimes return an error like the following: error Cannot query field "gatsbyPath" on type "BookWikiPage" graphql/template-strings. To fix this error, restart the development server manually by ending the process and running npm run develop again.

      With all the books on one page, even with collapsible sections things have gotten a little crowded, so the final step is to add some styling to make it easier for visitors to read the listing. Create a new stylesheet file at src/styles/books.css. You can do this in your file browser or with the command line from the root of your Gatsby project:

      • mkdir -p ./src/styles
      • touch ./src/styles/books.css

      Next, add the following CSS into the file:

      src/styles/books.css

      details {
        border: 1px dotted black;
        margin: 6px;
        padding: 6px;
      }
      
      .details-body {
        background-color: #eedeff;
        margin: 4px 0px 2px 12px;
        padding: 4px;
        border-radius: 4px;
      }
      
      .links {
        display: flex;
        justify-content: space-evenly;
      }
      

      This CSS adds a border around each book listing, spacing and margins within the listing, and spacing between the internal and external book links. After adding the CSS into the file, save and close it before moving on.

      Finally, update the book listing page template to pull this CSS file in:

      src/pages/books.js

      import { graphql, Link } from "gatsby";
      import * as React from "react";
      import Layout from "../components/layout";
      import Seo from "../components/seo";
      import "../styles/books.css";
      

      Save and close this file with the newly added CSS import line.

      To see the results, run the develop command again to bring up the development server and preview the new pages:

      You can now access your book listing page at localhost:8000/books/.

      Screenshot showing the book listings page, using the template file in the tutorial. Each section can be expanded to show the excerpt and links or collapsed to show just the title.

      You have now not only built a Gatsby source plugin from scratch, but also used it to generate pages based on a React template.

      Conclusion

      By following the steps in this tutorial, you have now finished building a custom source plugin that brings outside content into your Gatsby project and used it to power new pages within your site.

      There is a lot of depth to source plugins. If you are interested in following best practices and learning more advanced source plugin concepts, here are some areas that might interest you:

      If you would like to read more about Gatsby, check out the rest of the How To Create Static Web Sites with Gatsby.js series.



      Source link