Contentful ❤ Gatsby
by Stephan / 18 May '16
Assumptions
- Your Gatsby site is set up already, if not refer to the Gatsby post.
Set up content model
- Go to "Content model" in dashboard & add new "content type"
- Add content types as you please
Connect Contentful
Install Plugin
- Go to Gatsbyjs.org and get details on the plugin & install.
- Copy Delivery API object & add to gatsby-config.js
{
resolve: `gatsby-source-contentful`,
options: {
spaceId: `your_space_id`,
// Learn about environment variables: https://gatsby.dev/env-vars
accessToken: process.env.CONTENTFUL_ACCESS_TOKEN,
},
},
Set up environment variables & copy keys from Contentful dashboard
- Create .env.development * .env.production in root
- Add sensitive data in this the new .env files create, example: CONTENTFUL_ACCESS_TOKEN=yoursecretkey
- Require dotenv in gatsby.config
require('dotenv').config({
path: `.env.${process.env.NODE_ENV}`,
})
- Add variable to config:
accessToken: process.env.CONTENTFUL_ACCESS_TOKEN
Add query
Go to GraphiQL or Playground and test query. Once data received copy query. If images are used remember to update image source with Gatsby Image Fragment
Set up layout & components
Create Components
- Layout
- Header
- Footer
In the index.js file wrap your layout component around child elements
layout.js
---
const layout = ({children}) => {
return (
<>
<Navbar />
<main>{children}</main>
<Footer />
</>
)
}
Render Data
const PostsPageComponent = ({ data }) => {
const { allContentfulPost: { nodes: posts },
} = data
console.log("<<POSTS>>")
console.log(posts);
return (
<Layout>
<section>
{posts.map(post => {
console.log(post)
return <article key={post.id}>
<Image fluid={post.image.fluid} alt={post.title}></Image>
<h1>{post.title}</h1>
<Link to={`/posts/${post.slug}`}>Read more</Link>
</article>
})}
</section>
</Layout>
)
}
Set up template
Create new template folder inside src with template file inside, in this example post-template.js
Slug
Back in GraphiQL or Playground test your query for all posts to get your slug:
query GetPosts {
posts:allContentfulPost {
nodes {
slug
}
}
}
Gatsby-Node
Create in root: gatsby-node.js
Create function & return promise:
exports.createPages = async ({graphql, actions}) => {
const {createPage} = actions
const result = await graphql(`
query GetPosts {
posts:allContentfulPost {
nodes {
slug
}
}
}
`)
console.log('<< POST RESULT TEST >>')
console.log(JSON.stringify(result))
}
Restart server to apply gatsby-node changes, upon restart you should see this in your logs:
Fetch Contentful data: 506.187ms
success source and transform nodes - 0.626s
success building schema - 0.271s
<< POST RESULT TEST >>
{"data":{"posts":{"nodes":[{"slug":"third-post"},{"slug":"second-post"},{"slug":"first-post"}]}}}
success createPages - 0.020s
Loop over posts with forEach method because it is an array, look at POST RESULT TEST to get the structure: {"data":{"posts":{"nodes":[{"slug":"third-post"},{"slug":"second-post"},{"slug":"first-post"}]}}}
exports.createPages = async ({graphql, actions}) => {
...
`)
console.log('<< POST RESULT TEST >>')
console.log(JSON.stringify(result))
result.data.posts.nodes.forEach((post) => {
createPage({
path:`/posts/${post.slug}`,
component:path.resolve(`src/templates/post-template.js`),
context: {
slug:post.slug,
}
})
})
}
Remember to add Node.js Path Module to your gatsby-node.js file: const path = require('path')
Test props in post template
Add the following to post-template.js
const PostTemplate = (props) => {
console.log('<< PROPS RECEIVED FROM POST PAGE >>')
console.log(props)
return (
<div>
post template, coming soon!
</div>
)
}
Browse to site, direct yourself to 404 page and use links to test if you receiving the props.
Query single post
In GraphiQL or Playground test single post as well as query variables to be sure everything is working.
query GetSinglePost($slug:String) {
post: contentfulPost(slug: {eq:$slug}) {
title
postText {
postText
}
image {
fluid {
src
}
}
}
}
Add query variable to test
{
"slug": "first-post"
}
If all good and you get the correct result, use code exporter to copy the page query
Add query to post template page
post-template.js
----------------
const PostTemplate = ({ data }) => <pre>{JSON.stringify(data, null, 4)}</pre>
export const query = graphql`
query GetSinglePost($slug:String) {
post: contentfulPost(slug: {eq: $slug}) {
title
postText {
postText
}
image {
fluid {
src
}
}
}
}
`
export default PostTemplate
Clean it up and add the rest of the components
Test data commented out in finished example below. Destructure data you going to use, in this case: title, image, postText Import gatsby-image Import layout component and Wrap layout component around return
post-template.js
----------------
// Test DATA:
// const PostTemplate = ({ data }) => <pre>{JSON.stringify(data, null, 4)}</pre>
const PostTemplate = ({ data: { post: { title, image: { fluid }, postText: { postText } } } }) => {
return <Layout>
<div>
<Link to="/posts">Back to Posts</Link>
<h1>Single Post</h1>
</div>
<section>
<article>
<Image fluid={fluid} alt={title} />
</article>
<article>
<h1>{title}</h1>
<p>{postText}</p>
</article>
</section>
</Layout>
}
Add styles
Using css modules in pages for this example. More info on CSS modules here
Global CSS will be used for other styles