Learning to Build a Image Gallery Site with JSON Data File

Note: This is part 1 of three-parts Learning GatsbyJs series. This learning post is still in active development and updated regularly.

Displaying images in a site plays an important role for user experience. Main selling point of Gatsby is its image optimization and lazy loading features. Handling optimization of images manually in a website can be a challenging process like cropping images, implementing lazy-loading of images, etc. A website with optimized images load faster and offers better user experiences.

Images often account for most of the downloaded bytes on a page. As a result, optimizing images can often yield some of the largest byte savings and performance improvements: the fewer bytes the browser has to download, the less competition there is for the client’s bandwidth and the faster the browser can download and render content on the screen. Page Speed Insights Doc | Google

Gatsbyjs with its gatsby-image, a react component is designed to work seamlessly with Gatsby sites. To quote from the Gatsby DocGatsby-image combines Gatsby’s native image processing capabilities with advanced image loading techniques to easily and completely optimize image loading for your sites“.

The gatsby-image component offers the following features:

  • Loads the optimal size of image for each device size and screen resolution.
  • Holds the image position while loading so your page doesn’t jump around as images load.
  • Uses the “blur-up” effect i.e. it loads a tiny version of the image to show while the full image is loading.
  • Alternatively provides a “traced placeholder” SVG of the image.
  • Lazy loads images which reduces bandwidth and speeds the initial load time.
  • Uses WebP images if browser supports the format.

The objective of this three-part part Gatsby learning series is to explore how Gatsby uses images from JSON file and creating a basic image gallery site similar to Kyle Mathews’s Gatsbygram example site.

Learning series
Part 1: Getting started to work from JSON data (this post)
Part 2: Learning to create a photo gallery from JSON with images
Part 3: Building a simple image gallery site from JSON data

The objective of this part 1 of the three part series is to getting started to using hard-coded JSON data file to build a simple Gatsby site.

Creating a Pages In Gatsby

In Gatsby a simple page component (eg. src/page/ . ) can be created without any data source as shown below:

// a page component
import React from "react"

const myPage = () => (
  <div>
    <h1>Hello Gatsby World!</h1>
    <p>This page is created without any data.</p>
  </div>
)

export default myPage

To create Gatsby pages dynamically (eg. without hard-coded data), it uses gatsby-node.js with Gatsby Node APIs, described in detail in a previous post. However, a page component can also be created with gatsby-node.js without using Gatsby Data  or with hard-coded data such as JSON data file.

Objective

The main objective of this post is learn how to create a Page component from JSON data containing both images & text contents (eg. portfolio page).

This post was inspired by Denny Tek’s Part 8: Creating a Project Page from JSON data of her Building a Personal Site with Gatsby.

Starting Point

As a starting point for this post, the working site used in Part 3 of this learning series was used. To start fresh, a new site named ‘hello-plugin‘ needs to be setup the Gatsby default starter hello-world as described previously. How to add markdown posts to a fresh installed site is described in previously in this post.

Step 1: Create the JSON file

Create a basic JSON data file inside data directory as /data/projects.json as shown below:

[
  {
    "title": "My First Project",
    "date": "2019-10-15",
    "description":
      "Lorem ipsum dolor sit amet, consectetur adipisicing elit. Harum 
       repudiandae, autem eos voluptatum assumenda cum laborum iure nam 
       in accusantium.",
    "url": "https://mysite.com",
    "thumbnailImage": "./images/nature-1.jpg"
  },
  {
    "title": "My Second Project",
    "date": "2019-10-20",
    "description":
      "Lorem ipsum dolor sit amet, consectetur adipisicing elit. Harum 
       repudiandae, autem eos voluptatum assumenda cum laborum iure nam 
       in accusantium.",
    "url": "https://mysite.com",
    "thumbnailImage": "./images/nature-2.jpg"
  },
//..
{
    "title": "My Fifth Project",
    "date": "2019-11-05",
    "description":
      "Lorem ipsum dolor sit amet, consectetur adipisicing elit. Harum 
       repudiandae, autem eos voluptatum assumenda cum laborum iure nam 
       in accusantium.",
    "url": "https://mysite.com",
    "thumbnailImage": "./images/nature-5.jpg"
  }
]

For images, an image sub-folder was created within data folder as /data/images/ and some unsplash images were placed. Appropriate path to the image folder was added in JSON data file (lines: 10, 20, .. 31).

Step 2: Add Gatsby’s transformer-json plugin

1. Install gatsby-transformer-json plugin

#! add plugin with yarn or npm
yarn add gatsby-transformer-json

#! install plugin with npm
npm install --save gatsby-transformer-json

2. Configure the plugin in gatsby-config.js with gatsby-source-filesystem as follows:

// gatsby-config.js
module.exports = {
//...
  plugins: [
  //...other plugins
    `gatsby-transformer-json`,
    {
      resolve: `gatsby-source-filesystem`,
      options: {
        name: `data`,
        path: `${__dirname}/data`,,
      },
    },
    //... other plugins
  ],
}

Having the plugins installed & configured in gatsby-config.js, it is now possible to create a GraphQL query from the JSON data.

Tip: Before using GraphQL query, graphql components needs to be imported to page component as shown in step 4, line 3.

Step 3: Create GraphQL Query

After restarting the development server, the site can be viewed at the browser at localhost:8000/___graphql showin that the projects.json is available to query as allProjectsJson (see below).  The JSON transformer plugin creates one node for each project, and inside the node we can select the data we need for the project.

Figure: Screenshot of GraphQL query showing field under allProjectsJson (left) and query result (right).

In the GraphiQL query below, fields under childImagesSharp  a fragment GatsbyImageSharpFluid is used (lines: 14-16) to standardize formatting, as suggested in Gatsby tutorial.

//
export const projectsQuery = graphql`
  query {
    allProjectsJson(sort: { order: DESC, fields: [date] }) {
      edges {
        node {
          id
          title
          date
          description
          url
          thumbnailImage {
            childImageSharp {
              fluid(maxWidth: 1200) {
                ...GatsbyImageSharpFluid
              }
            }
          }
        }
      }
    }
  }
`;
Step 4: Create Project.js Page Component

Create a Project.js component page (as shown below) at src/pages/project.js as shown below. Take a note that in addition to the Layout component (line 4), graphql from gatsby (line 3), Image component from gatsby-image (line 5) and Button from components/button (line 6) are imported.

// src/pages/project.js
import React from "react"
import { graphql } from 'gatsby'
import Layout from "../components/layout"
import Img from 'gatsby-image'
import Button from '../components/button'

const ProjectsPage = ({ data }) => (
  <Layout>
      <h1 className="page-title">My Projects</h1>
    <div className="project-list">
      {data.allProjectsJson.edges.map(project => (
        <div key={project.node.id} className="project-list_item">
          <div className="project-list_thumbnail">
            <Img fluid={project.node.thumbnailImage.childImageSharp.fluid} />
          </div>
          <div className="project-list_content">
            <h4>{project.node.title}</h4>
            <div className="project-list_excerpt">
              {project.node.description}
            </div>
            <a href={project.node.url}>
              <Button buttonText="Visit the Website" />
            </a>
          </div>
        </div>
      ))}
    </div>
  </Layout>
);

export default ProjectsPage;
// .. graphql query

In the example above, the array of data.allProjectsJson.edges returned from the query are mapped to display our projects which includes node’s fluid properly as as thumbnailImage.childImageSharp.fluid as its value (line 15) .This renders the allProjectsJson.edges with lists of our projects (step: 6).

Step 5: Basic Styling of Project Page

For styling, basic CSS styling was added to src/styles/scss/projects.scss folder to display thumbnail image and text in a 2-column rows using css flex. as shown below:

/* src/styles/_projects.scss */
.project-list {
  margin: -4rem 0;
  &_content {
    padding: 1rem;
    text-align: left;
  }
  &_excerpt {
    padding-bottom: 2rem;
  }
  &_item {
    display: flex;
    flex-direction: column;

    @media (min-width: 600px) {
      flex-direction: row;
    }
    padding: 1rem;
    margin: -4rem 0;
    border-bottom: 2px solid #b2b2b2;
    margin-bottom: 1.5rem;
  }
  &_thumbnail {
    align-self: center;
    width: 100%;
  }
}
Step 6: View the Project.js Page Component in a Browser

After fresh re-starting the development server and the projects.json show in a browser at localhost:8000 (see below).

Figure: Screenshot of Project page component field under allProjectsJson query result (above; only partial screen shown). Image source: Unsplash.

Use Case Example: Creating a Gallery

After creating a simple portfolio in the previous, I wanted to expand on the previous project to creating a simple galley prototype. Using the same assets,

Step 1: Expanding JSON file

The data/projects.json file created in previous section (step 1) was expanded to include more images.

Step 2: Create GraphQL Query for Gallery Image display

The following GraphQL query to display gallery image is similar from the previous section except some node fields (eg. title, description, url) were excluded.

// graphql query
export const projectsQuery = graphql`
  query {
    allProjectsJson(sort: { order: ASC, fields: [date] }) {
      edges {
        node {
          id
          date
          thumbnailImage {
            childImageSharp {
              fluid(maxWidth: 400, maxHeight: 300) {
                ...GatsbyImageSharpFluid
              }
            }
          }
        }
      }
    }
  }
`;
Step 3: Create a Photogram.js Page Component

A page component src/pages/Photogram.js was created by modifying src/pages/Project.js from the previous section (shown below).

//src/pages/photogram.js
import React from "react"
import { graphql } from 'gatsby'
import Layout from "../components/layout"
import Img from 'gatsby-image'

const PhotogramPage = ({ data }) => (
 <Layout>
   <h1 className="page-title">My Photogram</h1>
    <div className="gallery">
     {data.allProjectsJson.edges.map(project => (
       <div key={project.node.id} className="item">
        <div className="item">
         <Img fluid={project.node.thumbnailImage.childImageSharp.fluid} />
        </div>
       </div>
      ))}
   </div>
  </Layout>
);

export default PhotogramPage;

// add graphql query
export const projectsQuery = graphql`
  query {
    allProjectsJson(sort: { order: ASC, fields: [date] }) {
      edges {
        node {
          id
          date
          thumbnailImage {
            childImageSharp {
              fluid(maxWidth: 400, maxHeight: 300) {
                ...GatsbyImageSharpFluid
              }
            }
          }
        }
      }
    }
  }
`;

In the example above, the GraphQL query (lines: 25-43) from the previous section was slightly modified (excluded title, description, url  properties) and adjusting its images sizes in fluid property.

To create image gallery, the code snippets used in the previous posts  (at section 5) were used as guide. Basically, the array of thumbnailImage node returned from the query is loop over as data.allProjectsImages.edges.map (line 11) and then pass each node‘s fluid property as thumbnailImage.childImageSharp.fluid as its value (lines: 10-18). This renders the childImageSharp node with multiple images in Gallery.

Step 3: Add Basic Styling for Image Gallery

The following basic CSS styling was added  as /src/styles/scss/gallery.scss to display images in a 3-colums grid.

/* src/styles/scss/gallery.css  */
.gallery {
  display: grid; 
  grid-gap: 10px; 
   grid-auto-flow: dense;
 } 

.gallery img {
 display: flex;
  width: 100%;
  height: auto;
  object-fit: cover;
  border-radius: 0.25rem;
}

@media (min-width: 480px) {
  .gallery {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    margin: 0 auto;
    grid-gap: 10px;
    grid-auto-flow: dense;
  }
  .gallery img {
    display: flex;
  }
}
Step 4: View the Photogram.js Component in a Browser

After fresh re-starting the development server and the photogram.json show in a browser at localhost:8000 (see below).

Screenshot of Photogram gallery page component (left) and its mobile view (right). Image source: Unsplash.

That was it! The next is to create a image-post.js template to display individual images and link image with its image-post template using createPage node API.

Wrapping Up

In this part 1 of three-part learning series, how to use JSON file to create page components to display portfolio & image gallery prototypes were discussed. In the next part, how to create image-post template to create & display single image-post with createPage Node API will be discussed.

Next Post: Learning to create a photo gallery from JSON with images

Useful resources

While preparing this post, I have referred the following references extensively. Please refer to original posts for more detailed information.