Skip to content
On this page

Directus Cloud

Everything you need to start building. Provisioned in 90 seconds. Starting at $15/month.

Get Started

Developer Blog

Getting Started with Directus and Gatsby

Published February 7th, 2024

Written By
Alvin Bryan
Alvin Bryan
Guest Author

In this blog post, we will create a static website using Gatsby and Directus as a Headless CMS. We'll make use of the official Directus Gatsby plugin which leverages into one of Gatsby's best features - image compression.

Before You Start

You will need:

Create a Gatsby Project

Create a new Gatsby project on your terminal and walk through the wizard:

sh
npm init gatsby

What would you like to call your site?
   Directus Gatsby Blog
What would you like to name the folder where your site will be created?
   directus-gatsby-blog
Will you be using JavaScript or TypeScript?
   JavaScript
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
Add the Google gtag script for e.g. Google Analytics
   Done
Shall we do this? (Y/n)
   Yes
npm init gatsby

What would you like to call your site?
   Directus Gatsby Blog
What would you like to name the folder where your site will be created?
   directus-gatsby-blog
Will you be using JavaScript or TypeScript?
   JavaScript
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
Add the Google gtag script for e.g. Google Analytics
   Done
Shall we do this? (Y/n)
   Yes

Navigate into the new directory and install dependencies:

sh
cd directus-gatsby-blog
npm install @directus/gatsby-source-directus
npm install gatsby-plugin-image gatsby-transformer-sharp gatsby-plugin-sharp
cd directus-gatsby-blog
npm install @directus/gatsby-source-directus
npm install gatsby-plugin-image gatsby-transformer-sharp gatsby-plugin-sharp

Start the development server

sh
npm run develop
npm run develop

Your website is live at http://localhost:8000/

Configuring the Directus Plugin

This will make each item in your collection into its own page on your Gatsby site.

Directus maintains an official plugin for Gatsby. That plugin turns your Directus content is turned into Gatsby source nodes. You can query these nodes everywhere in your Gatsby site, using the GraphQL API, just like you would with Markdown files.

First, you need to add and configure the Directus plugin, the and the other Gatsby plugins that act as dependencies. Update your gatsby-config.js file as follows:

js
module.exports = {
  siteMetadata: {
    title: `Directus Gatsby Blog`,
    siteUrl: `https://www.yourdomain.com`,
  },
  plugins: [
    "gatsby-plugin-image",
    "gatsby-plugin-sharp",
    "gatsby-transformer-sharp",
    {
      resolve: "@directus/gatsby-source-directus",
      options: {
        // Your project's URL
        url: "DIRECTUS_PROJECT_URL",
        auth: {
          // Directus user credentials
          email: "YOUR_EMAIL",
          password: "YOUR_PASSWORD",
        },
      },
    },
  ],
};
module.exports = {
  siteMetadata: {
    title: `Directus Gatsby Blog`,
    siteUrl: `https://www.yourdomain.com`,
  },
  plugins: [
    "gatsby-plugin-image",
    "gatsby-plugin-sharp",
    "gatsby-transformer-sharp",
    {
      resolve: "@directus/gatsby-source-directus",
      options: {
        // Your project's URL
        url: "DIRECTUS_PROJECT_URL",
        auth: {
          // Directus user credentials
          email: "YOUR_EMAIL",
          password: "YOUR_PASSWORD",
        },
      },
    },
  ],
};

Be sure to replace DIRECTUS_PROJECT_URL, YOUR_EMAIL, and YOUR_PASSWORD with values from your specific Directus project.

Using Global Metadata and Settings

In your Directus project, navigate to Settings -> Data Model and create a new collection called global. Under the Singleton option, select 'Treat as a single object', as this collection will have just a single entry containing global website metadata.

Create one text input field with a key of title.

Navigate to the content module and enter the global collection. Collections will generally display a list of items, but as a singleton, it will launch directly into the one-item form. Enter information in the title field and hit save.

A form named Global has one input - a title - with some text in it.

By default, new collections are not accessible to the public. Navigate to Settings -> Access Control -> Public and give Read access to the Global collection.

Next, we'll setup a static hook with a GraphQL query so can use this global metadata everywhere in our Gatsby site.

Create a new file named directus.js in your ./src directory (static queries only work in that folder).

js
import { useStaticQuery, graphql } from "gatsby";

export const useGlobalMetadata = () => {
  const { directus } = useStaticQuery(graphql`
    {
      directus {
        global {
          title
        }
      }
    }
  `);

  return directus.global;
};
import { useStaticQuery, graphql } from "gatsby";

export const useGlobalMetadata = () => {
  const { directus } = useStaticQuery(graphql`
    {
      directus {
        global {
          title
        }
      }
    }
  `);

  return directus.global;
};

Now let's get rid of all the boilerplate in our index page (src/pages/index.js) and replace it with the following:

js
import React from "react";
import { useGlobalMetadata } from "../directus";

const IndexPage = ({ data }) => {
  const pages = data.directus.pages;
  const global = useGlobalMetadata();

  return (
    <main>
      <h1>{global.title}</h1>
    </main>
  );
};

export default IndexPage;
import React from "react";
import { useGlobalMetadata } from "../directus";

const IndexPage = ({ data }) => {
  const pages = data.directus.pages;
  const global = useGlobalMetadata();

  return (
    <main>
      <h1>{global.title}</h1>
    </main>
  );
};

export default IndexPage;

Refresh your browser. You should see data from your Directus Global collection in your page.

Creating Pages with Directus

Create a new collection called authors with a single text input field called name. Create one or more authors.

Then, create a new collection called pages - make the Primary ID Field a "Manually Entered String" called slug, which will correlate with the URL for the page. For example hello-world will later correlate to the page localhost:3000/blog/hello-world.

Create the following fields in your posts data model:

  • a text input field called title
  • a WYSIWYG input field called content
  • an image relational field called image
  • a many-to-one relational field called author with the related collection set to authors

In your Access Control settings, give the Public role read access to the authors, pages, and directus_files collections.

Create a Gatsby template in ./src/templates/blog.js and populate it with the following:

jsx
import React from "react";
import { GatsbyImage, getImage } from "gatsby-plugin-image";

const BlogTemplate = ({ pageContext }) => {
  const { page } = pageContext;
  const image = getImage(page.image.imageFile);

  return (
    <div style={{ margin: "0 auto", maxWidth: "58ch" }}>
      <GatsbyImage image={image} alt={page.title} />
      <h1>{page.title}</h1>
      <section>{page.content}</section>
    </div>
  );
};

export default BlogTemplate;
import React from "react";
import { GatsbyImage, getImage } from "gatsby-plugin-image";

const BlogTemplate = ({ pageContext }) => {
  const { page } = pageContext;
  const image = getImage(page.image.imageFile);

  return (
    <div style={{ margin: "0 auto", maxWidth: "58ch" }}>
      <GatsbyImage image={image} alt={page.title} />
      <h1>{page.title}</h1>
      <section>{page.content}</section>
    </div>
  );
};

export default BlogTemplate;

In the template, we're expecting each item in our Directus collection to be passed as a page object via the pageContext property. We'll set this up now.

A the root of your project directory, create a gatsby-node.mjs file (note the .mjs):

jsx
import path from "path";

export const createPages = async ({ graphql, actions: { createPage } }) => {
  const result = await graphql(`
    {
      directus {
        pages {
          title
          content
          slug
          image {
            id
            imageFile {
              childImageSharp {
                gatsbyImageData(width: 400)
              }
            }
          }
        }
      }
    }
  `);

  result.data.directus.pages.forEach((page) => {
    createPage({
      path: `/blog/${page.slug}`,
      component: path.resolve(`./src/templates/blog.js`),
      context: {
        page,
      },
    });
  });
};
import path from "path";

export const createPages = async ({ graphql, actions: { createPage } }) => {
  const result = await graphql(`
    {
      directus {
        pages {
          title
          content
          slug
          image {
            id
            imageFile {
              childImageSharp {
                gatsbyImageData(width: 400)
              }
            }
          }
        }
      }
    }
  `);

  result.data.directus.pages.forEach((page) => {
    createPage({
      path: `/blog/${page.slug}`,
      component: path.resolve(`./src/templates/blog.js`),
      context: {
        page,
      },
    });
  });
};

Now each item in your Directus collection is a page, and you can navigate to it using its slug, like /blog/rabbit-facts. There, you will see your template file rendered.

A page shows an image, a header, and HTML content

Listing Blog Posts in an Index Page

Next, we want to create an index page that shows you all the items in our collection. We created individual pages for each item using the official plugin, which also made Gatsby nodes for us.

We can use the GraphQL API to query these nodes and list everything in our index page.

First, update the index page with the following:

jsx
import React from "react";
import { graphql, Link } from "gatsby";
import { GatsbyImage, getImage } from "gatsby-plugin-image";
import { useGlobalMetadata } from "../directus";

const IndexPage = ({ data }) => {
  const pages = data.directus.pages;
  const global = useGlobalMetadata();

  return (
    <main>
      <h1>{global.title}</h1>
      {pages.map((page) => {
        const image = getImage(page.image.imageFile);
        return (
          <article key={page.slug}>
            <GatsbyImage image={image} alt={page.title} />
            <h2>{page.title}</h2>
            <Link to={`/blog/${page.slug}`}>Read More</Link>
            <hr />
          </article>
        );
      })}
    </main>
  );
};

export const query = graphql`
  {
    directus {
      pages {
        title
        content
        slug
        image {
          id
          imageFile {
            childImageSharp {
              gatsbyImageData(width: 300)
            }
          }
        }
      }
    }
  }
`;

export default IndexPage;
import React from "react";
import { graphql, Link } from "gatsby";
import { GatsbyImage, getImage } from "gatsby-plugin-image";
import { useGlobalMetadata } from "../directus";

const IndexPage = ({ data }) => {
  const pages = data.directus.pages;
  const global = useGlobalMetadata();

  return (
    <main>
      <h1>{global.title}</h1>
      {pages.map((page) => {
        const image = getImage(page.image.imageFile);
        return (
          <article key={page.slug}>
            <GatsbyImage image={image} alt={page.title} />
            <h2>{page.title}</h2>
            <Link to={`/blog/${page.slug}`}>Read More</Link>
            <hr />
          </article>
        );
      })}
    </main>
  );
};

export const query = graphql`
  {
    directus {
      pages {
        title
        content
        slug
        image {
          id
          imageFile {
            childImageSharp {
              gatsbyImageData(width: 300)
            }
          }
        }
      }
    }
  }
`;

export default IndexPage;

Adding Navigation

To navigate to other pages from your index, you need to use the Link component from Gatsby, just like we did earlier.

This is how you would add a link to an about page:

diff
<h1>{global.title}</h1>
+ <nav>
+   <ul>
+     <li>
+       <Link to={`/about`}>Read More</Link>
+     </li>
+   </ul>
+ </nav>
<h1>{global.title}</h1>
+ <nav>
+   <ul>
+     <li>
+       <Link to={`/about`}>Read More</Link>
+     </li>
+   </ul>
+ </nav>

And then we can create an about.js in our src/pages folder like so:

js
import React from "react";
import { useGlobalMetadata } from "../directus";

const AboutPage = () => {
  const global = useGlobalMetadata();

  return (
    <main>
      <h1>{global.title}</h1>

      <p>YOUR_CONTENT</p>
    </main>
  );
};

export default AboutPage;
import React from "react";
import { useGlobalMetadata } from "../directus";

const AboutPage = () => {
  const global = useGlobalMetadata();

  return (
    <main>
      <h1>{global.title}</h1>

      <p>YOUR_CONTENT</p>
    </main>
  );
};

export default AboutPage;

Summary

Thats it! You've learned:

  • How to use the official Directus plugin to transform your content into source nodes
  • How to create static pages for each item in a Directus collection.
  • How to query your Directus content with Gatsby's GraphQL API.
  • How to put take your Directus remote images and optimize them with Gatsby's image processing pipeline.

Tell us what is missing

How helpful was this article?