Building a photo gallery app with Gatsby and GraphCMS
Tags: javascript, webdev, react, gatsby
Published: 2022-05-13 21:23:42

by [Jude Miracle](https://blog.openreplay.com/authors/jude-miracle)


Most modern websites and apps now make use of backend-only content management systems. With [GraphCMS](https://graphcms.com/) being one of the most popular to use in developing our web applications. With the help of a content management system, we won't have to spend much time setting up a server and database, writing APIs with which the front end can send and receive data to and from the backend. GraphCMS helps us deal with these hassles so that we can focus on building our application.

## Architecture for the Project

In this tutorial, we will look at connecting the GraphCMS to [Gatsby](https://www.gatsbyjs.com/plugins/gatsby-source-graphql/?=gatsby-source-graphql) and fetch some data using Graphql. We will look at getting started with GraphCMS and setting it up. Since we are building a photo gallery app, we will make all image data available in our GraphCMS API and attach Gatsby.js to the front end. We will create a model and update the content in our GraphCMS admin dashboard. We will then be able to request to get each of the photos displayed on our front end.

Before we dive into building our project, let's first understand what a Headless CMS is and its need.

## What's a Headless CMS, and why do we need one?

Headless CMS is a content management system that provides a way to manage content. Instead of having your content together in a particular output, it provides your content as data over an API, making it very easy to share your data across multiple devices.

Earlier, Traditional CMS gave us the idea of having all our content, templates, and assets (images, videos) in a single environment. However, this method of serving the content is insufficient concerning how data can be distributed. Having a Headless CMS system is a developmental advance that prompts an even better way of managing content. The structure is flexible, content is treated as data and provided over an API to be served across multiple devices, and content editors work in tailored real-time collaboration environments.

## Why use Gatsby?

Gatsby is an open-source framework that enables developers to make blazing fast, search engine-friendly, secure, and powerful websites. It uses a React-based framework and innovative data layer that makes integrating different content, APIs, and services into one web experience effortless. Gatsby builds websites and apps with modern front-end technologies such as ReactJS, Webpack, GraphQL, ES6+ JavaScript, and CSS.

Gatsby provides a way for us to source data from various headless CMSs such as WordPress, GraphCMS, Strapi, Contentful, etc., through plugins.

## Why use GraphCMS?

GraphQL offers many advantages such as predictability, performance, development speed, stability, and flexibility. GraphCMS adopts that strategy and does it admirably. GraphCMS makes it easy to integrate with Netlify, Vercel, and Gatsby Cloud for a quick preview. It has remote fields which allow you to pull multiple sources of information into a single API. It automatically generates GraphQL queries from content. It also has built-in localization and locale-based publishing, allowing editors to localize and translate content into any language. You can check out the other notable features GraphCMS offers [here](https://graphcms.com/features).

Now that you have a little grasp of what a headless CMS means, the need for it, why we choose Gatsby for our front end and GraphCMS to handle our backend. Let's dive into building our jam stack project.

## Prerequisites

To follow this tutorial, you need:

- [Node.js](https://nodejs.org/en/)
- [npm](https://www.npmjs.com/)
- [Gatsby](https://www.gatsbyjs.com/docs) installed 
- [GraphCMS](https://graphcms.com/) account

## Prepare GraphCMS

First, you need to sign up for a GraphCMS account so that you can access our admin panel. You will be given the option to either sign up with Facebook, Google, or Github or fill out some information. It's up to you.

![Signing up for GraphCMS](https://blog.openreplay.com/static/45b7fb49b71542cff50c8a00f3224c3c/20a35/image01.png)

After signing up, you'll be directed to this page. Click on the plus icon that says blank for a new project. 

![Creating a project](https://blog.openreplay.com/static/1ec03dbf63d2dd8f808b49ddcbf15815/7b5cb/image02.png)

Here you can give your project a name. In our case, we're naming the project `Photo gallery`. You will be required to choose a server where your data will be stored. Then we will create the project.

![Picking a plan for your project](https://blog.openreplay.com/static/057589096564bfa5816cbb8ce6d25144/7b5cb/image03.png)

Another cool feature GraphCMS provides is its generous free plan where you have access to all the basic functions: 5 project users, localization, content stages, etc. In our case, we are making use of a free plan.

## Creating Schemas

On our admin dashboard, on the left, below `environments`, click on `Schemas`, and we will create a model.

![Creating a model](https://blog.openreplay.com/static/b31ab9c9ecf59d3377af5936b8e3608c/20a35/image04.png)

When we click `add model`, a modal opens up where we can create our photo content type and name it `Photos`. We clicked on `continue`, and now we can add fields to our content.

![The "name" field](https://blog.openreplay.com/static/3a35c43b64f9cb5336b4b0e3519ce04e/20a35/image05.png)

Click on the `single line text` on the right-hand side, and a modal will pop up, as shown in the image above. We will be creating three text fields: the photo gallery app's title, the photo's name, and the photo's source. We will check the use as the title field because we will use it as the project's title.

![The "description" field](https://blog.openreplay.com/static/943331b9e819b4f7126f46bb5c6b2dea/20a35/image06.png)

We will click on another field and select `multi-line text`, but this time we name its `description` as it will be the name and the source of the photo. As shown in the image above, we also checked the allow multiple values option.

![The "image" field](https://blog.openreplay.com/static/a570b5d22b5dff95990cffadab87ce3c/20a35/image07.png)

Lastly, we created an `asset picker` field. You can find it by scrolling down to the field options. We named it `image`, and this will be the image that will be displayed.

![The results](https://blog.openreplay.com/static/e14e473bbdb7e908c5da3c9f3ec9675d/20a35/image08.png)

## Adding our Photos

We go to `content editing` below the schema and click on the `create item` button at the top right side.

![Editing content](https://blog.openreplay.com/static/39109f7db75c70ba43d34f31cf481fde/20a35/image09.png)

Here you can see the first photo created in the image above with the fields we created earlier. We fill within the input fields with the specified information.

- The `name` field is for the name of each photo.
- The `description` field is for the photo description.
- The `image` field is for the image that will be displayed on our photo gallery app.

To add an image to the app, click on add an existing image, a modal will appear, and you will see the `upload` button; click on it. You will see the options provided to you, so it's uploaded to you to select your choice. Then click on `upload`.

![Adding assets](https://blog.openreplay.com/static/2bc358041eef70f1e5f29157feefa6c6/20a35/image10.png)

After filling out the information and adding the image, click on the save and publish button. Then you can go back and create more items. In our case, we are creating nine items.

## Setting up Roles and Permissions

Before we begin to query this data inside Gatsby, we need to first get our API endpoint and set public permission to open or query any published content. 

![Setting permissions](https://blog.openreplay.com/static/cd20d16fa4c454062311f9e059e90bc4/20a35/image11.png)

Now head to `settings`, and inside the API access, you will see the endpoints. By default, it's on content from stage publishing and hit `create permission`. This means anybody accessing this URL can make a public API request without any authentication required.

## Setting up our Gatsby Front end

In this section, we will set up Gatsby and build the photo gallery app UI. so let's start installing Gatsby CLI. Gatsby command-line interface (CLI) is a tool that allows you to snappily create new Gatsby-powered sites and run commands for developing Gatsby sites. On your terminal, run the subsequent command:

```
npm install -g gatsby-cli
// to check if it is installed
gatsby --version
```

With the command line interface installed, we can now create our Gatsby project.

```
gatsby new
```

You will be asked a few questions.

```
What would you like to call your site?
· Photo Gallery App
✔ What would you like to name the folder where your site will be created?
.Desktop/ photo-gallery-app
✔ Will you be using a CMS?
· No (or I'll add it later)
✔ Would you like to install a styling system?
· No (or I'll add it later)
✔ Would you like to install additional features with other plugins?
· Done
Thanks! Here's what we'll now do:

    🛠  Create a new Gatsby site in the folder my-first-gatsby-site

? Shall we do this? (Y/n) › Yes
🎉  Your new Gatsby site Photo Gallery App has been successfully created
```

Once it is done installing, let's first `cd` into our project and install some plugins and dependencies.

```
cd photo-gallery-app
```

What we are going to install:

- [Tailwind CSS](https://tailwindcss.com/docs/guides/gatsby) as our UI framework
- [Gatsby plugins](https://www.gatsbyjs.com/plugins/) - `gatsby-source-graphql`: It is used to source data for our GraphCMS projects.

## Configuring our dependencies

Let's start by installing [Tailwind CSS](https://tailwindcss.com/docs/guides/gatsby). In our project folder, let's run the following commands:

```
npm install -D tailwindcss postcss autoprefixer gatsby-plugin-postcss
npx tailwindcss init -p
```

We have installed tailwind CSS, `postcss`, `autoprefixer`, and `gatsby-plugin-postcss`.

```javascript
// gatsby.config.js
module.exports = {
  plugins: [
    'gatsby-plugin-postcss',
    // ...
  ],
}
```

On your `gatsby-config.js` file, enable the `gatsby-plugin-postcss`.

```javascript
// tailwind.config.js

module.exports = {
  content: [
    "./src/**/*.{js,jsx,ts,tsx}",
  ],
  theme: {
    extend: {},
  },
  plugins: [],
}
```

Add paths to all of your template files in your `tailwind.config.js` file.

```
@tailwind base;
@tailwind components;
@tailwind utilities;
```

Create a `./src/styles/global.css` file and add the `@tailwind` directives for each of the Tailwind layers.

```javascript
// gatsby-browser.js

import './src/styles/global.css';
```

Create a `gatsby-browser.js` file at the root of your project if it doesn't already exist, and import your newly-created `./src/styles/global.css` file.

## Install the GraphCMS plugin 

We will be using CLI to install the plugin by running this command on your terminal:

```
npm install gatsby-source-graphql
```

With `gatsby-source-graphql`, you can integrate third-party APIs, like GitHub's, APIs of services like GraphCMS, and your custom GraphQL API.

## Configuring the GraphCMS plugin 

Add the following code to the `gatsby-config.js` file.

```javascript
module.exports = {
  siteMetadata: {
    siteUrl: "https://www.yourdomain.tld",
    title: "PhotoGallery",
  },
  plugins: [
    'gatsby-plugin-postcss',
    {
      resolve: 'gatsby-source-graphql',
      options: {
        typeName: 'GraphCMS',
        fieldName: 'gcms',
        url: 'https://api-eu-west-2.graphcms.com/v2/ckxcnz96d3srk01xi0skjbq9g/master',
      }
    },
  ],
};
```

Here we are configuring the graphCMS `graphql` plugin to be able to query our data through graphCMS API.

- Line 11: we specify the arbitrary name for the remote schema query type.
- Line 12: field under which the remote schema will be accessible. You can use this in your Gatsby query
- Line 13: URL to query from

## Query the data in Gatsby GraphiQL

Now that the API endpoint is set up, we will be able to query the data with Gatsby's GraphiQL playground, and we can shape the query we want to use to display the data here.

First of all, we need to start our server before accessing the Gatsby GraphiQL UI.

```
gatsby develop
```

We used this command to start our server. Now that our server has started, we run our site and the Graphql playground on the link provided to us.

![Starting our server](https://blog.openreplay.com/static/356b272bffdd17c7728a1f6806478f69/029bd/image12.png)

By clicking on the links provided, it will bring up the Gatsby GraphQl UI, where we can shape the data we want to query.

```
query myQuery {
  gcms {
    photo {
      id
      name
      description
    }
  }
}
```

You'll see the available fields where we can select the `gcms` field we defined in the `gatsby-source-graphql` plugin.

Choosing the fields we created in GraphCMS and then running the query will show our defined data.

## Using Asset Transformation API

We will also need to grab the images that we want to display. We do this by making use of asset transformation API. We will be able to grab our image URLs, and we can extend them by passing arguments on to them. You can read more about the asset [transformation API](https://graphcms.com/docs/api-reference/content-api/assets#transformations).

```
image {
  url(transformation: {image: {resize: {width: 500, height: 500, fit: clip}}})
}
```

Here we pass on arguments to our image URL by grabbing the width and height. We've also passed an argument of `fit` and given it a value of `clip` to resize the image to fit within the specified parameters without distorting, cropping, or changing the aspect ratio.

## Display GraphCMS Data to our Gatsby Client

Use the `graphql` gatsby export to query the data from the GraphCMS endpoint. On the `pages/index.js` add the `graphql` export the `gatsby` imports.

```javascript
import { useStaticQuery, graphql } from "gatsby";
```

At the very bottom define the query:

```javascript
export default function Gallery() {
  const data = useStaticQuery(graphql`
  query PhotoPage{
    gcms{
        photo{
            id
            name
            description
            image{
                url(transformation: {image: {resize: {width: 500, height: 500, fit: clip}}})
            }
        }
    }
}

const photos = data.gcms.photo;
```
    
## Open Source Session Replay

_[OpenReplay](https://github.com/openreplay/openreplay) is an open-source alternative to FullStory and LogRocket. It gives you full observability by replaying everything your users do on your app and showing how your stack behaves for every issue. OpenReplay is self-hosted for full control over your data._

![replayer.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1647097041609/Qi6sMOaaB.png)

_Happy debugging for modern frontend teams - [start monitoring your web app for free](https://github.com/openreplay/openreplay)._

## Building out the application UI

As I stated earlier, we will be using Tailwind CSS for the styling of this application
inside the `Gallery` function code block, add the following code:

```javascript
return (
  <div className="min-h-screen w-full flex justify-center items-center my-20 mx-0">
    <div className="text-xl bg-white w-5/6 my-0 mx-auto max-w-4xl shadow-lg py-11 px-5 rounded-2xl lg:max-w-6xl lg:p-24">
      <header className="flex items-center justify-center flex-col lg:flex-row lg:justify-between">
        <h1 className="text-4xl font-black tracking-tighter">PhotoGallery</h1>
        <form className="mt-2 w-4/5 max-w-xs relative">
          <input 
            type="text"
            placeholder="search images"
            className="border-b-2 mb-2 text-xl outline-none text-gray-400 bg-transparent w-full"
          />
          <img 
            src={search}
            alt="search btn"
            height="20px"
            width="20px"
            className="absolute right-0 top-0 opacity-50 text-gray-400"
          />
        </form>
      </header>
        <div className="md:grid grid-cols-4 mt-12 gap-5">
      {photos.map(photo => (
        <div key={photo.id} className="w-full h-80 relative overflow-hidden rounded-lg transition-shadow duration-35 ease hover:shadow-lg">
          <img 
            src={photo.image.url}
            alt={photo.name}
            className="h-full w-full object-cover"
          />
          <div className="w-full p-7 absolute bg-white bottom-0 text-xs">
            <h3 className="font-bold">{photo.name}</h3>
            <p>{photo.description}</p>
          </div>
        </div>
      ))}
      </div>
    </div>
  </div>
)
```

The output should be equivalent because of the results of the Gatsby GraphQL query we created:

```javascript
{
  "data": {
    "gcms": {
      "photo": [
        {
          "id": "ckxcqscq818i00d5256lsafgz",
          "name": "Safe-passage",
          "description": [
            "Unsplash - unknown"
          ]
        },
        {
          "id": "ckxcrasb418oh0d524cldkhz1",
          "name": "Vercel cup",
          "description": [
            "Unsplash - Kevin-canlas"
          ]
        },
        {
          "id": "ckxcrg4fs193k0b61yu121wp4",
          "name": "Black bitcoin coin",
          "description": [
            "Unsplash - Executium"
          ]
        },
        {
          "id": "ckxcrjqu819510d57tu4a2dsb",
          "name": "Blue Nike Shoe",
          "description": [
            "Unsplash -  Alexander-rotker"
          ]
        },
        {
          "id": "ckxcs7fgo19cz0d57ozjdlio1",
          "name": "Fried Potatoes",
          "description": [
            "Pexels - Dzenina Lukac"
          ]
        },
        {
          "id": "ckxcsbsvc19g60j58db5j4m5v",
          "name": "Bird Swiming",
          "description": [
            "Pixabay - Johnny B. Goode"
          ]
        },
        {
          "id": "ckxcsi8co19iq0j58uxb4c6xw",
          "name": "Pink Flower",
          "description": [
            "Pexel - Lisa"
          ]
        },
        {
          "id": "ckxcsneb419jx0d57puw2ikwh",
          "name": "Dog Hunting",
          "description": [
            "Pikwizard- Retriever"
          ]
        },
        {
          "id": "ckxcsr3so19me0j589rbjcf6v",
          "name": "Sea Lion",
          "description": [
            "Pixabay - Kev"
          ]
        }
      ],
      "assets": [
        {
          "imagePhotos": [
            {
              "image": {
                "url": "https://media.graphcms.com/resize=fit:clip/2GPaCYMvRI2j6lByh6p0"
              }
            }
          ]
        },
        {
          "imagePhotos": [
            {
              "image": {
                "url": "https://media.graphcms.com/resize=fit:clip/llrUKVLgRKyt9Q4ygcyd"
              }
            }
          ]
        },
        {
          "imagePhotos": [
            {
              "image": {
                "url": "https://media.graphcms.com/resize=fit:clip/EvbB0oHpQMGYeVkGk0yO"
              }
            }
          ]
        },
        {
          "imagePhotos": [
            {
              "image": {
                "url": "https://media.graphcms.com/resize=fit:clip/RxufeLgNRCmiyZ93xzeu"
              }
            }
          ]
        },
        {
          "imagePhotos": [
            {
              "image": {
                "url": "https://media.graphcms.com/resize=fit:clip/ZdUwUZ5bQGuQmaKnmUUP"
              }
            }
          ]
        },
        {
          "imagePhotos": [
            {
              "image": {
                "url": "https://media.graphcms.com/resize=fit:clip/vfOeohQWSLKzJqveSP4C"
              }
            }
          ]
        },
        {
          "imagePhotos": [
            {
              "image": {
                "url": "https://media.graphcms.com/resize=fit:clip/nXtEvGxjQwiPWamYYSno"
              }
            }
          ]
        },
        {
          "imagePhotos": [
            {
              "image": {
                "url": "https://media.graphcms.com/resize=fit:clip/VN0vYJGQkafa4AnIcFB8"
              }
            }
          ]
        },
        {
          "imagePhotos": [
            {
              "image": {
                "url": "https://media.graphcms.com/resize=fit:clip/sIXBYDLHQJq5IOYP9hI7"
              }
            }
          ]
        }
      ]
    }
  },
}
```

This is what our app looks like:

![The final result!](https://blog.openreplay.com/static/7aa9ba48505139ca643e2101fd8176df/1a152/image13.png)

## Conclusion

At the end of this tutorial, we looked at how we can connect the GraphCMS backend to our [Gatsby front end](https://www.gatsbyjs.com/guides/graphcms/) and fetch some data using GraphQL. In the process, we created a photo gallery app and model type to build the application. We also changed the roles and permissions that allow us to perform CRUD operations like reading and updating. We created a page that displays the information queried from our GraphCMS endpoint. If you'd like to learn more about what GraphCMS can do, check out its outstanding features on its [website](https://graphcms.com/).

All code for this article can be found in [Github](https://github.com/am-miracle/photo-gallery-app).

[![newsletter](https://blog.openreplay.com/static/e6b18eda7a327511296a2f27692f9c70/e9957/newsletter.png)](https://newsletter.openreplay.com/)