0
I’m trying to create a simple Byline
component to use in my MDX posts that displays the site author with a href="mailto:[email protected]"
and a time
tag with the date the post is given in its frontmatter.
I’ve tried following this Gatsby tutorial to learn how to query frontmatter through GraphQL in a component.
I don’t really know React nor GraphQL but I understand that the first one (author
) is queried through site.siteMetadata.author.name
(I think?).
As for the second one, since it uses the post’s frontmatter, I would assume I’m supposed to use mdx.frontmatter.date
.
I’m not getting any of this to work. I get these errors:
Cannot read properties of undefined (reading ‘site’)
Cannot read properties of undefined (reading ‘mdx’)
I also get this error:
There was an error in your GraphQL query:
Variable "$id" is not defined by operation "BylineQuery".
GraphQL request:10:19
9 | }
10 | mdx(id: { eq: $id }) {
| ^
11 | frontmatter {
GraphQL request:2:3
1 |
2 | query BylineQuery {
| ^
3 | site {
This is the complete component:
import * as React from 'react'
import { graphql } from 'gatsby'
const Byline = ({data }) => {
return (
<div className="byline">
<address>Av <a href="mailto:[email protected]" rel="author">{data.site.siteMetadata.author.name}</a></address>
<time dateTime={data.mdx.frontmatter.computerDate}>{data.mdx.frontmatter.humanDate}</time>
</div>
)
}
export const query = graphql`
query BylineQuery {
site {
siteMetadata {
author {
name
}
}
}
mdx(id: { eq: $id }) {
frontmatter {
computerDate: date(formatString: "YYYY-MM-DD")
humanDate: date(formatString: "D. MMMM YYYY", locale: "nb")
}
}
}
`
export default Byline
gatsby-node.js
:
const path = require(`path`)
const { createFilePath } = require(`gatsby-source-filesystem`)
exports.createPages = async ({ graphql, actions, reporter }) => {
const { createPage } = actions
// Define a template for blog post
const blogPost = path.resolve(`./src/templates/blog-post.js`)
// Get all markdown blog posts sorted by date
const result = await graphql(
`
{
allMdx(
sort: { fields: [frontmatter___date], order: ASC }
limit: 1000
) {
nodes {
id
fields {
slug
}
}
}
}
`
)
if (result.errors) {
reporter.panicOnBuild(
`There was an error loading your blog posts`,
result.errors
)
return
}
const posts = result.data.allMdx.nodes
// Create blog posts pages
// But only if there's at least one markdown file found at "content/blog" (defined in gatsby-config.js)
// `context` is available in the template as a prop and as a variable in GraphQL
if (posts.length > 0) {
posts.forEach((post, index) => {
const previousPostId = index === 0 ? null : posts[index - 1].id
const nextPostId = index === posts.length - 1 ? null : posts[index + 1].id
createPage({
path: post.fields.slug,
component: blogPost,
context: {
id: post.id,
previousPostId,
nextPostId,
},
})
})
}
}
exports.onCreateNode = ({ node, actions, getNode }) => {
const { createNodeField } = actions
if (node.internal.type === `Mdx`) {
const value = createFilePath({ node, getNode })
createNodeField({
name: `slug`,
node,
value,
})
}
}
exports.createSchemaCustomization = ({ actions }) => {
const { createTypes } = actions
createTypes(`
type SiteSiteMetadata {
author: Author
siteUrl: String
social: Social
}
type Author {
name: String
summary: String
}
type Social {
twitter: String
instagram: String
mail: String
}
type MarkdownRemark implements Node {
frontmatter: Frontmatter
fields: Fields
}
type Frontmatter {
title: String
description: String
date: Date @dateformat
}
type Fields {
slug: String
}
`)
}
Byline.js
:
(Currently just testing.)
(Located in components
directory.)
import React from "react"
import { graphql } from "gatsby"
export default function Byline({ data: { mdx } }) {
return (
<div>
<h1>{mdx.frontmatter.title}</h1>
</div>
)
}
export const pageQuery = graphql`
query BylineQuery($id: String) {
mdx(id: { eq: $id }) {
id
body
frontmatter {
title
}
}
}
`
gatsby-config.js
module.exports = {
siteMetadata: {
title: `Magnus Kolstad`,
author: {
name: `Magnus Rengård Kolstad`,
summary: `Summary`,
description: "Artikler skrevet av Magnus Kolstad",
},
description: `Description`,
siteUrl: `https://kolstadmagnus.no/`,
social: {
mail: `[email protected]`,
instagram: `kolstadmagnus`,
twitter: `KolstadMagnus`,
youtube: `UC7QpsGiWwVc9lmnIJvA9OLA`
},
},
plugins: [
`gatsby-plugin-image`,
{
resolve: `gatsby-source-filesystem`,
options: {
name: `blog`,
path: `${__dirname}/content/blog`,
},
},
{
resolve: `gatsby-source-filesystem`,
options: {
name: `images`,
path: `${__dirname}/src/images`,
},
},
{
resolve: `gatsby-source-filesystem`,
options: {
name: `pages`,
path: `${__dirname}/src/pages/`,
}
},
{
resolve: `gatsby-plugin-mdx`,
options: {
defaultLayouts: {
default: require.resolve(`./src/components/layout.js`),
},
gatsbyRemarkPlugins: [
{
resolve: `gatsby-remark-images`,
options: {
maxWidth: `900000000000`,
linkImagesToOriginal: false,
backgroundColor: `none`,
},
},
{
resolve: `gatsby-remark-responsive-iframe`,
options: {
wrapperStyle: `margin-bottom: 1.0725rem`,
},
},
`gatsby-remark-prismjs`,
`gatsby-remark-copy-linked-files`,
`gatsby-remark-smartypants`,
],
extensions: [`.md`, `.mdx`],
},
},
`gatsby-transformer-sharp`,
`gatsby-plugin-sharp`,
{
resolve: `gatsby-plugin-manifest`,
options: {
name: `Gatsby Starter Blog`,
short_name: `GatsbyJS`,
start_url: `/`,
background_color: `#ffffff`,
display: `minimal-ui`,
icon: `src/images/gatsby-icon.png`,
},
},
`gatsby-plugin-react-helmet`,
],
}
2 Answers
Reset to default
0
Extending Hasan’s answer:
First of all, here you wrote a page query. Page query only works in
pages. Make sure your component is in the pages directory.
That is partially true. Page queries also work in templates, which means that they are also pages but they aren’t located in the src/pages
folder but src/templates
(assuming the guide’s project structure).
Despite the query looks as it should be:
export const query = graphql`
query BylineQuery($id: String) {
site {
siteMetadata {
author {
name
}
}
}
mdx(id: { eq: $id }) {
frontmatter {
computerDate: date(formatString: "YYYY-MM-DD")
humanDate: date(formatString: "D. MMMM YYYY", locale: "nb")
}
}
}
Check it in the GraphiQL playground (localhost:8000/___graphql
) with a hardcoded id
if needed or changing the filter to another handy field.
The fact is that the key part is that you need to pass a id
from your gatsby-node.js
to the template using the context as it is inferred in the tutorial. For example::
posts.forEach(({ node }, index) => {
createPage({
path: node.fields.slug,
component: path.resolve(`./src/templates/blog-post.js`),
// values in the context object are passed in as variables to page queries
context: {
title: node.title,
id: node.id
},
})
})
The title
and id
variable will be exposed in the blog-post.js
template (under ./src/templates
) and can be used to filter your posts (in this case) using those values.
Simplifying, gatsby-node.js
creates your template pages (posts, etc) and to query the specific data for each post, you need to filter the data using a lifted context variable sent via context.
5
-
To me, this example looks like just the thing I'm looking for: gatsbyjs.com/docs/mdx/programmatically-creating-pages/…. Yet I can't run the component—I keep getting the "Cannot read properties of undefined (reading 'mdx')" warning. What am I missing out on here? Do I have to do something with my
gatsby-node.js
file? If so, what precisely?– Magnus KolstadDec 28, 2021 at 16:57
-
Look closely, you are not lifting the
$id
in your query yet you're using it before so it breaks. You may be missing other configurations but you haven't share it. Add them in your question because it may be the source or the explanation of why the nodes are missing but certainly you have an issue in the query. share the gatsby-node.js too to see how are you generating the pages– Ferran BuireuDec 28, 2021 at 18:01
-
Added gatsby-node.js and component.
– Magnus KolstadDec 28, 2021 at 18:21
-
gatsby-config.js
is still missing. Have you tried usingquery BylineQuery($id: String)
? Have you tried the query in the GraphiQL playground? (hardcoding the id or using the title as a filter)– Ferran BuireuDec 29, 2021 at 5:51
-
I added
gatsby-config.js
. Yes,query BylineQuery($id: String)
is what I'm currently using inByline.js
. I will try messing around with GraphiQL later today.– Magnus KolstadDec 29, 2021 at 9:17
-1
First of all, here you wrote a page query. Page query only works in pages. Make sure your component is in the pages directory. And the error comes from your query where you used a dynamic argument "$id" but you didn’t pass the variable.
export const query = graphql`
query BylineQuery($id: String) {
site {
siteMetadata {
author {
name
}
}
}
mdx(id: { eq: $id }) {
frontmatter {
computerDate: date(formatString: "YYYY-MM-DD")
humanDate: date(formatString: "D. MMMM YYYY", locale: "nb")
}
}
}
`
your query should be like this. Check the documentation ( https://www.gatsbyjs.com/docs/how-to/querying-data/page-query/ )
0
Not the answer you're looking for? Browse other questions tagged
or ask your own question.
or ask your own question.
|