Building a Personal Site with Gatsby

Part 8: Creating a Project Page from JSON data

January 11, 2019

Note: This is a 9 part post that begins here: Part 1: Introduction and Setup

For my personal website, I wanted to have a portfolio page that listed all of my projects. I didn't want to hardcode the projects and I didn't need to create separate pages for each project, so I looked to create a page using JSON data.

Create a src/pages/projects.js file, src/data folder, and a src/data/projects.json file. Also create a src/images/projects folder to store your project thumbnails. I grabbed some from lorempixel. Add your images to the projects folder and add their paths to your data.

src/data/projects.json
[
  {
    "title": "My First Project",
    "date": "2018-08",
    "description":
      "Lorem ipsum dolor sit amet, consectetur adipisicing elit. Harum repudiandae, autem eos voluptatum assumenda cum laborum iure nam in accusantium.",
    "url": "https://example.com",
    "thumbnailImage": "../images/projects/project-1.jpg"
  },
  {
    "title": "My Second Project",
    "date": "2018-09",
    "description":
      "Lorem ipsum dolor sit amet, consectetur adipisicing elit. Harum repudiandae, autem eos voluptatum assumenda cum laborum iure nam in accusantium.",
    "url": "https://example.com",
    "thumbnailImage": "../images/projects/project-2.jpg"
  }
]

Install gatsby-transformer-json and include it in your gatsby-config.js. You also have to make sure the data folder is readable by the filesystem.

shell
npm install --save gatsby-transformer-json

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

You will be able to query your data as 'allProjectsJson' in GraphQL. If your data were in a file called 'stuff.json', you would query it with 'allStuffJson'. Try it out with GraphiQL. Note that we're now using GatsbyImageSharpFluid rather than GatsbyImageSharpFixed.

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

and above that:

src/pages/projects.js
import React from 'react';
import { graphql } from 'gatsby';
import Layout from '../components/layout';
import SEO from '../components/seo';
import Img from 'gatsby-image';import Button from '../components/button';
const ProjectsPage = ({ data }) => (
  <Layout>
    <SEO
      title="Home"
      keywords={[`gatsby`, `application`, `react`, `portfolio`]}
    />
    <h1>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">            <h2>{project.node.title}</h2>            <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;

// ...your graphql query

Here, we have imported the Button component we created in part 2 and then we're mapping over data.allProjectsJson.edges to display our projects.

Create a src/styles/_projects.scss file, import it in src/styles/styles.scss and add the following:

src/styles/_projects.scss
.project-list {
  margin: 0;
  &__content {
    padding: 1rem;
    text-align: left;
  }
  &__excerpt {
    padding-bottom: 2rem;
  }
  &__item {
    display: flex;
    flex-direction: column;
    @media (min-width: 800px) {
      flex-direction: row;
    }
    padding: 1rem;
  }
  &__thumbnail {
    align-self: center;
    width: 100%;
  }
}