A Gallery App built with Gatsby and GraphCMS
Tags: webdev, gatsby, graphql
Published: 2022-06-29 11:45:05

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

Most modern websites and apps now make use of backend-only [content management systems](https://en.wikipedia.org/wiki/Content_management_system) (CMS) and [GraphCMS](https://graphcms.com/) is one of the most popular. With the help of a CMS, we won't have to spend much time setting up a server and database, or writing APIs with which the frontend can send and receive data to and from the backend. GraphCMS helps us deal with these hassles to focus on building our application.
 
In this tutorial, we will look at how we can connect the GraphCMS to [Gatsby](https://www.gatsbyjs.com/plugins/gatsby-source-graphql/?=gatsby-source-graphql) and fetch some data using [GraphQL](https://graphql.org/). 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 frontend. We will create a model and update the content in our GraphCMS admin dashboard. We would then be able to request to get each of the photos displayed on our frontend.

To follow this tutorial, you need [Node.js](https://nodejs.org/en/), [NPM](https://www.npmjs.com/), [Gatsby](https://www.gatsbyjs.com/docs), and a [GraphCMS](https://graphcms.com/) account.


## Our tools

Before we dive into building our project, let's first understand what a Headless CMS is and the need for it, and let's also give a look at the rest of the tools that we'll use.

### Why use a headless CMS?

A headless CMS is a content management system that provides a way to manage content. Still, 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 it 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, where 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, and secure, powerful websites using a React-based framework and innovative data layer that makes integrating different content, APIs, and services into one web experience extremely simple. Gatsby builds websites and apps with modern frontend technologies such as ReactJS, Webpack, GraphQL, modern ES6+ JavaScript, and CSS. Gatsby allows us to source data from various headless CMSs such as WordPress, GraphCMS, Strapi, Contentful, etc., effortlessly through the use of 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 and has a built-in localization, locale-based publishing — that allows your editors to localize and translate content into any language of their choice. 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 and its need, why we choose Gatsby for our frontend and GraphCMS to handle our backend. Let's dive into building our jam stack project.

## Setting up the back end with 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 by filling out some information. It's up to you.

![create project](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. 

![project name](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`, and you will be required to choose a server where your data will be stored. Then we will create the project.

![free plan](https://blog.openreplay.com/static/057589096564bfa5816cbb8ce6d25144/7b5cb/image03.png)

Another cool feature GraphCMS provides is its generous free plan where you can access all the essential functions: 5 project users, localization, content stages, and so on. 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.

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


When we click `add model`, a modal opens 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.


![fields](https://blog.openreplay.com/static/3a35c43b64f9cb5336b4b0e3519ce04e/20a35/image05.png)


On the right-hand side, click on the `single line text`, 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 title.



![description](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 photo's name and source. We also checked the allow multiple values option, as shown in the image above.



![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, and we name it `image`. This will be the image that will be displayed.


![schemas](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.


![save and publish](https://blog.openreplay.com/static/39109f7db75c70ba43d34f31cf481fde/20a35/image09.png)


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 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, so it's uploaded to you to select your choice. Then click on `upload`.


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


After filling out the information and adding the image, click 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 must first get our API endpoint and set public permission to open or query any published content. 


![permission](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 now make a public API request without any authentication required.


## Open Source Session Replay

_[OpenReplay](https://github.com/openreplay/openreplay) is an open-source, session replay suite that lets you see what users do on your web app, helping you troubleshoot issues faster. OpenReplay is self-hosted for full control over your data._

![replayer.png](https://static.openreplay.com/images/banner-blog.png)

_Start enjoying your debugging experience - [start using OpenReplay for free](https://github.com/openreplay/openreplay)._


## Setting up the front end with Gatsby

In this section, we will set up Gatsby and build the photo gallery app UI. Let's get started by 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 know if it is installed
gatsby --version
```

We can now create our Gatsby project with the command line interface installed.

```
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 installed, let's first `cd photo-gallery-app` into our project and install some plugins and dependencies. 
- [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.

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`.

```
// tailwind.config.js

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

Add paths to all 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 Tailwind layer.

```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.

Now, to configure the 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 can query the data with Gatsby's GraphiQL playground. We can shape the query we want to use to display the data here.

First, we need to start our server before we can access 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.

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

Clicking on the links provided 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 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 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; 
```    

## 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:

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

## Conclusion

At the end of this tutorial, we looked at how to connect the GraphCMS backend to our [Gatsby frontend](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, you can check out its outstanding features on its [website](https://graphcms.com/).

Check out the [Github code](https://github.com/am-miracle/photo-gallery-app) if you please.


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