Getting Started with Directus and Gatsby
Published February 7th, 2024
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:
- Node.js and a code editor installed.
- A Directus project - follow the quickstart guide to create one.
- Knowledge of Gatsby and React.
Create a Gatsby Project
Create a new Gatsby project on your terminal and walk through the wizard:
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:
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
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:
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.
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).
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:
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 toauthors
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:
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
):
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.
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:
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:
<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:
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.