Use Gatsby.js And TakeShape To Make Your JAMstack Site Sweeter
Tags: gatsby, tutorial, learning, webdev
Published: 2018-11-16 20:42:41

---
title: Use Gatsby.js And TakeShape To Make Your JAMstack Site Sweeter
published: true
description: TakeShape and Gatsby.js got together like peanut butter and jelly. Upgrade your skills by learning how to use them together
tags: gatsby, tutorial, learning, webdev
cover_image: https://thepracticaldev.s3.amazonaws.com/i/6w70s2868agay38ghzlt.jpg
canonical_url: https://www.takeshape.io/articles/use-gatsby-js-and-takeshape-to-make-your-jamstack-site-sweeter/
---

![TakeShape and Gatsby](https://thepracticaldev.s3.amazonaws.com/i/mezesbggcbufgpidb0cg.gif)

_This is part 4 of our 4 part series. Check out [part 1](https://www.takeshape.io/articles/how-to-build-a-design-portfolio-with-takeshape/) to get an introduction to TakeShape and learn content modeling and content creation. In [part 2](https://www.takeshape.io/articles/how-to-build-a-jamstack-website-with-takeshape/),  you’ll learn how to create a static site that loads data from [TakeShape](https://www.takeshape.io/). In [part 3](https://www.takeshape.io/articles/how-to-publish-a-jamstack-site-to-netlify-in-less-than-2-minutes-with-takeshape/), you’ll learn how to quickly deploy your new static site onto Netlify for a straightforward, secure, and speedy hosting solution._

**TLDR:** Short-circuit this tutorial and start playing around with the completed site. We won’t be offended! [Login](https://app.takeshape.io/login) and create a new project with “Shape Portfolio” template. Then clone the completed project repo, check out the `README.md` and start jamming.

```bash
> git clone https://github.com/takeshape/takeshape-samples
> cd takeshape-samples/shape-portfolio-gatsbyjs
```

## The Bold And Adventurous Should Continue
You’ve reached the final part of this series, which showed you how to create a static-site portfolio with [TakeShape](https://www.takeshape.io/). If you’ve been following along from the start you’re achieved quite a lot! You’ve created a content model and GraphQL API using TakeShape, used TakeShape’s static site generator to load your content into a fully customizable static site, and deployed your static site with Netlify.

In this conclusion, you’re going to take your portfolio site to the next level by learning how to build it with [Gatsby.js](https://www.gatsbyjs.org/), a static site generator that layers modern web tools like React.js and Webpack on top of the speedy and secure foundation of a JAMstack website. Gatsby is another great JAMstack tool for generating a JAMstack site with data from GraphQL APIs, especially when it comes to highly-interactive web applications. And TakeShape makes for a wonderful CMS backend for all of your Gatsby sites since every TakeShape project has a GraphQL API that easily integrates with Gatsby.

In this walkthrough, you’ll get started with Gatsby and translate your existing portfolio website into a React-based website. This is the most advanced walkthrough in this series, so if you haven’t already read through the previous articles, we recommended that you at least skim them first. 

## Hello Gatsby!
Get started by cloning the Gatsby starter project “hello world” and making sure everything works. Install the required libraries and then run the developer site with `npm start`.

```bash
> git clone https://github.com/gatsbyjs/gatsby-starter-hello-world shape-portfolio-gatsbyjs
> cd shape-portfolio-gatsbyjs
> npm install
> npm start
```

Visit `localhost:8000` in your browser to see the starter site running!

## Connect Gatsby to TakeShape

Since they’re both built around GraphQL data, Gatsby and TakeShape fit together like Lego bricks. After you provide TakeShape as a datasource to Gatsby, you’ll be able to query your data right from your pages and components.

To start, install the `gatsby-source-graphql` and `dotenv` plugins:

```bash
> npm install gatsby-source-graphql dotenv
```

Then create a `gatsby-config.js` file in the project’s root to configure the plugin:

```javascript
require("dotenv").config();

module.exports = {
    plugins: [
        {
            resolve: "gatsby-source-graphql",
            options: {
                typeName: "TS",
                fieldName: "takeshape",
                // Url to query from
                url: `https://api.takeshape.io/project/${process.env.TAKESHAPE_PROJECT}/graphql`,
                // HTTP headers
                headers: {
                    'Content-Type': 'application/json',
                    'Authorization': `Bearer ${process.env.TAKESHAPE_TOKEN}`,
                },
                // Additional options to pass to node-fetch
                fetchOptions: {},
            },
        },
    ]
};
```

You’ll notice that this configuration uses `TAKESHAPE_PROJECT` and `TAKESHAPE_TOKEN` values from our local environment. We’ll keep these values in our environment so that we don’t inadvertently commit these secrets to version control.

The Gatsby starter project should already contain a `.gitignore` file that includes `.env`, but you might want to double check, just in case.

Then, [create a read-only TakeShape API token](https://www.takeshape.io/docs/creating-an-api-key/) and copy your project’s ID from its URL in the TakeShape web client. Finally, create a file named `.env` in the root of your project that looks like this:

```bash
TAKESHAPE_PROJECT=<YOUR-PROJECTS-ID>
TAKESHAPE_TOKEN=<YOUR-API-TOKEN>
```

### Test your connection

Start your Gatsby development server with `gatsby develop`. As long as the configuration is correct, you’ll see the server start successfully and be able to test your connection to TakeShape with the built-in GraphiQL IDE.

Once the server has started, navigate to `http://localhost:8000/___graphql` in your browser. You’ll see a page that looks like the API Explorer in TakeShape—because it’s the same software!

Test that you can access your portfolio projects through Gatsby with this GraphQL query:

```graphql
query {
  takeshape {
    getProjectList {
      total
    }
  }
}
```

As you type this in, you should see the different fields autocomplete. Then, when you run the query you should see the total number of portfolio projects you’ve created in TakeShape so far. As you can see, Gatsby can easily talk to TakeShape, figure out the schema for your entire project, and let you pull that data right into your Gatsby project.

Now, we can start recreating our portfolio in React!

## Migrating from Nunjucks to React

We’ll start by creating pages for each project in your portfolio, then move onto the simpler task of recreating the home and about pages.

But before we do any of that, we need to migrate the base layout and structure over to React!

### Layout

First we’ll create a folder named `layouts` within the `src` directory of our project. Then we’ll create a file named `default.js`. You can do this in two commands, from the root of your project:

```bash
> mkdir src/layouts && touch src/layouts/default.js
```

Our `default.js` layout will be very similar in purpose to the `default.html` layout in the Nunjucks template you created for your `shape-portfolio` in part 2, though since it’s built with React, it’ll look slightly different:

```javascript
import React from "react";
import PropTypes from "prop-types";

import Header from "../components/header";
import Footer from "../components/footer";
import Menu from "../components/menu";
import "./default.css";

const Layout = ({children}) => (
    <>
        <Header siteTitle="Shape Portfolio"/>
        <div className="container">
            <Menu/>
            <div className="main">{children}</div>
            <Footer/>
        </div>
    </>
);

Layout.propTypes = {
    children: PropTypes.node.isRequired
};

export default Layout;
```

The purpose of this component is to provide a basic layout from which all your pages can inherit. By wrapping your other components with this layout, you can ensure that all the pages in your project maintain a consistent appearance and don’t duplicate any functionality. You’ll make `children` a required property of the Layout component, since it should always be acting as a wrapper for something else, whether that’s a list of projects, a project itself, or something else entirely.

Next create a `default.css` file in the same `src/layouts` directory. You’ll notice that it gets imported right into this `default.js` file as well. Now you might be saying, “Importing CSS into JS? That’ll never work!” Well since Gatsby comes with Webpack built in, it’ll extract and bundle all your CSS when it’s time to build your site. This has the benefit of letting you keep your styles right alongside your components.

For now, go ahead and copy the `main.css` styles from the `shape-portfolio` project into `default.css`. You won’t be diving into advanced styling in this walkthrough, although the [fancier CSS-in-JS techniques that Gatsby supports with additional plugins](https://www.gatsbyjs.org/docs/styling/) are worth exploring in more depth.

#### Header & Footer

In your `default.js` layout, the Header, Footer, and Menu are all abstracted into standalone components. You’ll start by creating the Header file, which will provide all the the `<head>` tags for your project.

Start by adding the `react-helmet` and `gatsby-plugin-react-helmet` packages to your project:

```bash
> npm install react-helmet gatsby-plugin-react-helmet
```

Then add `gatsby-plugin-react-helmet` to your list of plugins in your `gatsby-config.js` file:

```javascript
module.exports = {
    plugins: [
	'gatsby-plugin-react-helmet',
   	 {
   	   resolve: "gatsby-source-graphql",
   	   …
      }
  ]
}
```

[React Helmet](https://github.com/nfl/react-helmet) is an easy-to-use component for managing the document head of all of your pages. It will append the `<head>` tags you provide to the beginning of every page you include it on—and since you’re including it in your default layout, it’ll be included in every page.

In your `src` directory, create a new `components` directory and inside it create a new file named `header.js` that looks like this:

```javascript
import React from "react";
import Helmet from "react-helmet";

const Header = ({siteTitle}) => (
    <Helmet>
        <html lang="en"/>
        <title>{siteTitle}</title>
        <meta charSet="utf-8"/>
        <meta name="viewport" content="width=device-width, initial-scale=1"/>
        <link
            href="https://fonts.googleapis.com/css?family=Work+Sans:400,500,600,700"
            rel="stylesheet"
        />
    </Helmet>
);

export default Header;
```

That’s all it takes to create a Header component, to be imported and used by your default layout.

Next create your site’s footer in `src/components/footer.js`. This component is simpler:

```javascript
import React from "react";

const Footer = () => (
    <footer className="footer">
        <p>
            © {new Date().getFullYear()}{" "}
            <a
                href="https://takeshape.io"
                target="_blank"
                rel="noopener noreferrer nofollow"
            >
                TakeShape Inc.
            </a>
        </p>
    </footer>
);

export default Footer;
```

You’ll notice that this footer contains our first piece of dynamic content: the copyright date will automatically reflect the current year by using the `new Date().getFullYear()` function. The ability to use JavaScript inside of components is one of the most powerful aspects of React!

#### Menu

The final layout component that you’ll create will also be the first one to query data from your TakeShape project. To enable this functionality, you’ll use the [`StaticQuery` API](https://www.gatsbyjs.org/docs/static-query/#static-query) provided by Gatsby. With `StaticQuery`, a component can individually request the data that it needs to render. This is a great fit for your Menu component, since it’ll render the same on every page regardless of what other content the page contains.

In order to render images from TakeShape, you’ll also use the `getImageUrl` API provided by TakeShape’s `takeshape-routing` package.

First, install the `takeshape-routing` package:

```bash
> npm install takeshape-routing
```

Then, create a `src/components/menu.js` component that looks like this:

```javascript
import React from "react";
import { Link, StaticQuery, graphql } from "gatsby";
import { getImageUrl } from "takeshape-routing";

import routes from "../routes";

const menuQuery = graphql`
  query {
    takeshape {
      projects: getProjectList {
        items {
          name
        }
      }
      about: getAbout {
        socialProfiles {
          profileUrl
          socialNetwork
          socialNetworkIcon {
            path
          }
        }
      }
    }
  }
`;

const Menu = ({ data }) => (
  <nav className="menu">
    <div className="menu__container">
      <p className="site-name">
        <Link to="/">
          <strong>Shape Portfolio</strong>
        </Link>
      </p>
      <ul className="nostyle">
        {data.takeshape.projects.items.map((project, i) => (
          <li key={i}>
            <Link to={routes.project(project.name)}>{project.name}</Link>
          </li>
        ))}
      </ul>
      <hr />
      <Link to="/about">About</Link>
      <ul className="nostyle inline">
        {data.takeshape.about.socialProfiles.map((profile, i) => (
          <li className="social-profile" key={i}>
            <a
              href={profile.profileUrl}
              target="_blank"
              rel="noopener noreferrer"
            >
              <img
                className="social-network-icon"
                src={getImageUrl(profile.socialNetworkIcon.path)}
                alt="Social network icon"
              />
              <span className="social-network-name">
                {profile.socialNetwork}
              </span>
            </a>
          </li>
        ))}
      </ul>
    </div>
  </nav>
);

export default () => (
  <StaticQuery query={menuQuery} render={data => <Menu data={data} />} />
);
```

The menu consists of two objects: a query for the data we want to include and a component to render it. At the bottom of the file, the two objects get passed to Gatsby’s `StaticQuery` API, which takes on the work of fetching the data and rendering the component.

The menu also makes use of Gatsby’s `Link` component. Notice how you use `Link` to link within your site, as you would link to a project page, while you use a standard anchor to link outside your site, as you would link to a social network. When you use `Link`, Gatsby can load these new pages dynamically with Javascript, preventing additional page loads. It’s a slick out-of-the-box feature.

Finally, the `Link` uses the function `routes.project` to set its destination. You’ll need to create that function yourself. Luckily, that’s the next step!

### Project Pages

You’ll want to create a standalone page for every project, so that you can link directly to different projects in your portfolio. But, each page should also be created dynamically, based on data from TakeShape. So how can you go about creating pages dynamically?

First, create a new file named `gatsby-node.js` in your site’s root directory. This will contain code that’s executed by Gatsby’s build process. You’ll plug-in to Gatsby’s `createPages` API to create a standalone page for each of your portfolio’s projects.

First create the file:

```bash
> touch gatsby-node.js
```

Then add your function:

```javascript
const path = require("path");
const routes = require("./src/routes");

exports.createPages = async ({ actions, graphql }) => {
  const { data } = await graphql(`
    query {
      takeshape {
        projects: getProjectList {
          items {
            _id
            name
          }
        }
      }
    }
  `);

  data.takeshape.projects.items.forEach(({ _id, name }) => {
    actions.createPage({
      path: routes.project(name),
      component: path.resolve("./src/components/Project.js"),
      context: {
        projectId: _id
      }
    });
  });
};
```

This function will first query TakeShape for a list of all the project names and IDs. Then, for each project, you’ll use the `actions.createPage` API to create its standalone page. Each page will need three things:
1. A path, which you’ll generate using a routing function using the project’s name.
2. A component, which you’ll create to render the project’s page.
3. A context, which you’ll provide to each project page’s individual GraphQL query,

#### Creating your Project path

You’ll create the function to generate a route for the project. First create a file named `routes.js` in the `src` directory and add the `slugify` package to your project.

```bash
> touch src/routes.js  
> npm install slugify
```

Next, in `src/routes.js`, add a function for creating routes for your projects based on their names:

```javascript
const slugify = require("slugify");

exports.project = function(name) {
  const slug = slugify(name.toLowerCase());
  return `/projects/${slug}/`;
};
```

As you add other dynamically-generated pages to your portfolio, you can add their routing rules to this same file.

#### Creating your Project component and query

Your component will render the entire project, which you’ll wrap in the `Layout` component you already created. This will render a complete page for each project!

Start by creating a file named `Project.js` in your `src/components` directory that looks like this:

```javascript
import React from "react";
import { graphql } from "gatsby";
import { getImageUrl } from "takeshape-routing";

import Layout from "../layouts/default";

export const ProjectMetadata = ({ startDate, endDate, client }) => {
  const startYear = new Date(startDate).getFullYear();
  const endYear = endDate ? new Date(endDate).getFullYear() : null;
  const includeEndYear = endYear && startYear !== endYear;
  return (
    <div className="project__metadata">
      <p className="project__metadata">
        {startYear}
        {includeEndYear ? `&endash; ${endYear}` : ""}
      </p>
      {client ? (
        <p>
          <a href={client.url} target="_blank" rel="noopener noreferrer">
            {client.name}
          </a>
        </p>
      ) : (
        ""
      )}
    </div>
  );
};

export const Project = ({ project }) => (
  <article className="project">
    <header>
      {project.coverImage ? (
        <img
          className="project__cover-image"
          alt={project.coverImage.description}
          src={getImageUrl(project.coverImage.path, {
            h: 600,
            w: 1200,
            fit: "crop"
          })}
        />
      ) : (
        ""
      )}
      <h1>{project.name}</h1>
      <ProjectMetadata
        startDate={project.startDate}
        endDate={project.endDate}
        client={project.client}
      />
    </header>
    <div
      className="project__description"
      dangerouslySetInnerHTML={{ __html: project.description }}
    />
  </article>
);

export default ({ data }) => (
  <Layout>
    <Project project={data.takeshape.project} />
  </Layout>
);

export const query = graphql`
  query($projectId: ID!) {
    takeshape {
      project: getProject(_id: $projectId) {
        name
        startDate
        endDate
        coverImage {
          description
          path
        }
        client {
          name
          url
        }
        description: descriptionHtml
      }
    }
  }
`;
```

Starting from the bottom, this file exports a query for an individual project and component that renders the complete project page, both of which are used by the `createPage` API.  The query accepts the `context` you provided to the `createPage` API and fetches data for each Project page. It then provides it to the page with the `data` parameter. The page wraps the Project component in the Layout component in order to render the complete page.

The `Project` component in this file provides the logic for actually rendering each project. It also uses a `ProjectMetadata` subcomponent to handle the logic for formatting the project’s metadata.

### Homepage

Your homepage will provide a list of your projects, which we’ll create in a separate reusable component. The homepage will provide its own query for a list of projects, then pass it on to the `ProjectList`.

The Gatsby starter project you used will already contain a file named `index.html` in the `src/pages` directory. `src/pages` is a special directory in Gatsby: each file in this directory will automatically create its own standalone page, as long as the file exports at least a component.

Update your `src/pages/index.html` to be like this:

```javascript
import React from "react";
import { graphql } from "gatsby";

import Layout from "../layouts/default";
import ProjectList from "../components/ProjectList";

const IndexPage = ({ data }) => (
  <Layout>
    <ProjectList projects={data.takeshape.projects} className="main" />
  </Layout>
);

export default IndexPage;

export const query = graphql`
  query {
    takeshape {
      projects: getProjectList {
        items {
          name
          startDate
          coverImage {
            description
            path
          }
        }
      }
    }
  }
`;
```

Then, create a file named `ProjectList.js` in your `src/components` directory and make it look like this:

```javascript
import React from "react";
import { Link } from "gatsby";
import { getImageUrl } from "takeshape-routing";

import routes from "../routes";

const ProjectListItem = ({ project }) => {
  const startYear = new Date(project.startDate).getFullYear();
  const endYear = project.endDate
    ? new Date(project.endDate).getFullYear()
    : null;
  const includeEndYear = endYear && startYear !== endYear;
  return (
    <Link to={routes.project(project.name)} className="project">
      <img
        className="project__thumbnail"
        src={getImageUrl(project.coverImage.path, {
          h: 200,
          w: 300,
          fit: "crop"
        })}
        alt={project.description}
      />
      <p className="project__name">
        <strong>{project.name}</strong>
      </p>
      <p class="project__metadata">
        {startYear}
        {includeEndYear ? `&endash; ${endYear}` : ""}
      </p>
    </Link>
  );
};

const ProjectList = ({ projects }) => (
  <ul className="nostyle project-list">
    {projects.items.map((project, i) => (
      <li className="project-list--entry" key={i}>
        <ProjectListItem project={project} />
      </li>
    ))}
  </ul>
);

export default ProjectList;
```

In this file, the `ProjectList` component renders a set of `ProjectListItem` components. Each `ProjectListItem` displays some limited information about the project and links to the Project page you’ve already made. This component doesn’t require a query to be included with it, since it’s not a standalone page and the data is provided by the `index.js` file that includes it.

At this point, you should be able to run `gatsby develop`, navigate to `http://localhost:8000/`, and see a list of projects. Clicking on any one of them should take you to that project’s page. It’s alive!

### About and 404 Pages

Finally, you’ll finish off by creating a standalone About page and a standalone 404 page. For the about page, you can create a file `about.js` in `src/pages` that looks like this:

```javascript
import React from "react";
import { graphql } from "gatsby";
import { getImageUrl } from "takeshape-routing";

import Layout from "../layouts/default";

const AboutPage = ({ data }) => (
  <Layout>
    <article className="about">
      <img
        className="about__portrait"
        src={getImageUrl(data.takeshape.about.portrait.path, {
          h: 150,
          w: 150,
          fit: "crop"
        })}
      />
      <div
        className="about__biography"
        dangerouslySetInnerHTML={{ __html: data.takeshape.about.biography }}
      />
    </article>
  </Layout>
);

export default AboutPage;

export const query = graphql`
  query {
    takeshape {
      about: getAbout {
        biography: biographyHtml
        portrait {
          title
          description
          path
        }
      }
    }
  }
`;
```

The file should export your `AboutPage` component and a query so that the page can render on its own.

For the 404 page, you’ll just wrap a few lines with your `Layout` component and call it a day:

```javascript
import React from "react";
import Layout from "../layouts/default";

const NotFoundPage = () => (
  <Layout>
	<h1>NOT FOUND</h1>
	<p>You just hit a route that doesn&#39;t exist... the sadness.</p>
  </Layout>
);

export default NotFoundPage;
```

## Preview your production site

Running `gatsby develop` lets you run a development-friendly version of your site, with neat features like hot-reloading of components when you make changes.

To preview what your site will look like in production, you can use the `gatsby build` and `gatsby serve` commands:

* After running `gatsby build`, your website will be compiled and saved into a folder named `public` at the top of your project.
* After running `gatsby serve`, you can visit your production preview at `localhost:9000` in your browser.

## Deploy your fancy new site to Netlify

Now that your project is completed, it’s time to get it online! By using Gatsby, you’ll be circumventing TakeShape’s built-in static site generator, so you will need to set up your deployment system manually using a combination of Netlify’s continuous integration features and TakeShape’s webhooks functionality.

First make sure that your project is hosted in a remote Git repository, like on GitHub. Then create a **New site from Git** from the [Netlify sites page](https://app.netlify.com/account/sites). Authorize Netlify to access your project’s repository, then use the default build configuration. Netlify should automatically recognize that you’re using Gatsby as your static site generator and configure your build settings correctly. Then click **Deploy Site**.

Your first Deploy should fail. That’s ok! You haven’t added your `TAKESHAPE_PROJECT` and `TAKESHAPE_TOKEN` settings to your environment in TakeShape, so you should expect it to fail.

To fix this, go to your Netlify site’s settings and go to the “Build & Deploy” section. From there, you can edit your “Build environment variables” to securely add your TakeShape credentials. Click **Edit variables** and then add your variables for `TAKESHAPE_PROJECT` and `TAKESHAPE_TOKEN`. Click **Save**.

Then, go to your site’s Deploy section and click **Trigger deploy**. You’ll watch your deployment script run and log messages as it goes. At the end, you should see success messages and finally a declaration that your “Site is live”! Congrats!!! 🎉 If you go back to your site’s overview, you should see a link to your newly live site.

Finally you’ll want to set up a Build webhook *(Webhooks are available on all paid TakeShape plans)* so that you can tell Netlify to rebuild your site when you create, update, or delete your content in TakeShape. Back in your Netlify site’s “Build & Deploy” settings, look for the section labelled “Build hooks”. Click **Add build hook**, give it a name like “TakeShape”, click **Save**, and then copy the URL that gets created for you.

Then, in TakeShape, navigate to your project’s webhook settings in **Project Settings > Webhooks**. Configure a new webhook so that the URL you just copied is the **Webhook URL** and the **Resources** that trigger it are `Content: *`. And make sure you check the Create, Update, and Delete actions. Leave the **Secret** field blank and click **Save**.

Now, whenever you create a new content object, update an existing one, or delete a content object, it’ll trigger a new build of your site in Netlify. This way, your site is always up-to-date with your TakeShape project.

## Conclusion

If you’ve made it this far, congratulations! You dove head-first into the JAMstack and survived. Hopefully, you now have a strong understanding of the power of a configurable CMS, an automatically-generated GraphQL API, and a powerful React-powered static site. JAMstack is the future of web development and you’re now at the cutting edge.

If you have any feedback, comments, or questions on our series, don’t hesitate to reach out. Your feedback is always welcome through our in-app live chat or via [support@takeshape.io](mailto:support@takeshape.io).

## Interested In Headless CMS For Gatsby.js?

TakeShape is a headless GraphQL CMS that makes building JAMstack with Gatsby easier. At TakeShape we're building the best tools for managing content possible for the most creative designers and developers. Our project templates, make it easy to get started. Plus, pricing is downright affordable. [Sign up for a free account](https://app.takeshape.io/signup) and spend more time being creative!