I was trying to make two template for my projects details and blog post pages, but due to my lake of experience with Graphql & Gatsby I have managed to make it work for my projects, project details and blog posts but I have a bug which I couldn't find on my blog posts article template makes it not working??
here is my .md front matter
---
title: The Kimo-Coffee
stack: HTML & CSS
slug: the-kimo-coffee
date: 2022/01/03
key: projects
thumb: ../images/thumbs/coffee.png
featuredImg: ../images/featured/coffee-banner.png
---
and this is my blog page code (that's working fine)
import { graphql, Link } from "gatsby"
import React from "react"
import Layout from "../../components/Layout"
import * as styles from "../../styles/blog.module.css"
import { GatsbyImage, getImage } from "gatsby-plugin-image"
const posts = ({ data }) => {
const posts = data.articles.nodes
return (
<Layout>
<div className={styles.blog}>
<h2>Blog Posts</h2>
{posts.map(post => (
<Link to={"/blog/" + post.frontmatter.slug} key={post.id}>
<div className={styles.posts}>
<GatsbyImage
image={getImage(post.frontmatter.thumb)}
alt={post.frontmatter.title}
/>
<h3 className={styles.title}>{post.frontmatter.title}</h3>
<p className={styles.date}>{post.frontmatter.date}</p>
<p className={styles.stack}>{post.frontmatter.stack}</p>
<p className={styles.description}>
{post.frontmatter.description}
</p>
</div>
</Link>
))}
</div>
</Layout>
)
}
export default posts
export const query = graphql`
query BlogPosts {
articles: allMdx(
filter: { frontmatter: { key: { eq: "article" } } }
sort: { fields: frontmatter___date, order: DESC }
) {
nodes {
excerpt
slug
frontmatter {
key
date
title
stack
description
thumb {
childImageSharp {
gatsbyImageData(placeholder: BLURRED, layout: FULL_WIDTH)
}
}
}
id
}
}
}
`
here is my blog post details (which is not working)
import React from "react"
import { graphql, Link } from "gatsby"
import { MDXRenderer } from "gatsby-plugin-mdx"
import { GatsbyImage, getImage } from "gatsby-plugin-image"
import Layout from "../components/Layout"
import * as styles from "../styles/blog-details.module.css"
const blogPosts = ({ data, pageContext }) => {
const { body } = data.article
const { title, date, stack, featuredImg } = data.article.frontmatter
const { previous, next } = pageContext
return (
<Layout>
<div className={styles.details}>
<h2>{title}</h2>
<p>{date}</p>
<p>{stack}</p>
<div className={styles.featured}>
<GatsbyImage
image={getImage(featuredImg.childImageSharp.gatsbyImageData)}
alt={title}
/>
</div>
<article className={styles.body}>
<MDXRenderer>{body}</MDXRenderer>
</article>
<React.Fragment>
{previous && (
<Link to={previous.frontmatter.slug}>
<button>{previous.frontmatter.title}</button>
</Link>
)}
</React.Fragment>
<React.Fragment>
{next && (
<Link to={next.frontmatter.slug}>
<button>{next.frontmatter.title}</button>
</Link>
)}
</React.Fragment>
</div>
</Layout>
)
}
export default blogPosts
export const query = graphql`
query BlogDetails($slug: String) {
article: mdx(frontmatter: { slug: { eq: $slug }, key: { eq: "article" } }) {
body
frontmatter {
slug
key
title
date
stack
featuredImg {
childImageSharp {
gatsbyImageData(placeholder: BLURRED, layout: FULL_WIDTH)
}
}
}
}
}
`
and last this is my code for gatsby-node.js
const path = require("path")
const { createFilePath } = require(`gatsby-source-filesystem`)
exports.onCreateNode = ({ node, getNode, actions }) => {
const { createNodeField } = actions
if (node.internal.type === `Mdx`) {
const slug = createFilePath({ node, getNode, basePath: `pages` })
createNodeField({
node,
name: `slug`,
value: slug,
})
}
}
exports.createPages = async ({ graphql, actions }) => {
const { createPage } = actions
const results1 = await graphql(`
query content {
allMdx(filter: { frontmatter: { key: { eq: "projects" } } }) {
nodes {
frontmatter {
slug
title
}
}
}
}
`)
const results2 = await graphql(`
query content {
allMdx(filter: { frontmatter: { key: { eq: "article" } } }) {
nodes {
frontmatter {
slug
title
}
}
}
}
`)
const projectTemplate = path.resolve(`src/templates/project-details.js`)
const blogTemplate = path.resolve(`src/templates/blog-details.js`)
if (results1.errors || results2.errors) {
reporter.panicOnBuild(`Error while running GraphQL query.`)
return
}
const posts = results2.data.allMdx.nodes
posts.forEach((post, index) => {
const previous = index === post.length - 1 ? null : post[index + 1]
const next = index === 0 ? null : posts[index - 1]
createPage({
path: `/blog/${post.frontmatter.slug}`,
component: blogTemplate,
context: {
slug: post.frontmatter.slug,
previous,
next,
},
})
})
results1.data.allMdx.nodes.forEach(node => {
createPage({
path: `/projects/${node.frontmatter.slug}`,
component: projectTemplate,
context: { slug: node.frontmatter.slug },
})
})
}
sorry if my code is ugly, I'm pretty new to programming and i started with Gatsby since it makes me feel optimistic after learning javascript and react :)
There are a few things weird in your code that shouldn't be working despite your said it does.
In React, all components must be capitalized, otherwise, React will interpret them as HTML elements, and because normally a component name doesn't match an HTML tag, it will break because it won't exist.
In your case, both templates must be renamed to:
const Posts = ({ data }) => {}
And:
const BlogPosts = ({ data, pageContext }) => {}
The prompted error:
"There's not a page or function yet at /blog/undefined"
This means that the template is not valid because there is not a valid function template. Once capitalized, the issue should be gone.
In addition, double-check the markdowns to ensure that you have a slug
field in all of them.
You can also enter to localhost:8000/404
which in development, displays a list of all generated pages. You can check there if all the posts are being created correctly. If so, the issue is in your template. Otherwise, it's in your page generation.
Your issue is in the GraphQL query of the BlogPosts
, where the link to the article is not properly set. You have defined a query like:
query BlogPosts {
articles: allMdx(
filter: { frontmatter: { key: { eq: "article" } } }
sort: { fields: frontmatter___date, order: DESC }
) {
nodes {
excerpt
slug # <-- here's the issue
frontmatter {
key
date
title
stack
description
thumb {
childImageSharp {
gatsbyImageData(placeholder: BLURRED, layout: FULL_WIDTH)
}
}
}
id
}
}
}
Where the slug
is directly under nodes
node. At the same time, you are linking the slug as it was a child of the frontmatter
at:
<Link to={"/blog/" + post.frontmatter.slug} key={post.id}>
Which results in blog/undefined
path. You can check the path just hovering the link and seeing where is it pointing.
Ensure where it should be the slug field according to your data structure. I'd say that it should be inside the frontmatter
:
frontmatter {
slug # <-- add it here
key
date
title
stack
description
thumb {
childImageSharp {
gatsbyImageData(placeholder: BLURRED, layout: FULL_WIDTH)
}
}
}
Alternatively, just change the Link
structure like:
<Link to={"/blog/" + post.slug} key={post.id}>
Because of:
createNodeField({
node,
name: `slug`,
value: slug,
})
You are creating a slug
based on the filename, which is being inserted as a node
child, that's why the query was working but the references to that wasn't.