am new to gatsby and graphql and I came across a tutorial where it is mentioned to fetch all the data using .map. But I want to fetch only one element from the DB. So how do I do it?
import React from "react";
import Layout from "../components/layout";
import { useStaticQuery, graphql, Link } from "gatsby";
const Blogs = () => {
const data = useStaticQuery(
graphql`
query {
allMarkdownRemark(sort: { frontmatter: { date: ASC } }) {
edges {
node {
frontmatter {
title
date(formatString: "DD MM, YYYY")
}
excerpt
id
fields {
slug
}
}
}
}
}
`
);
return (
<Layout>
<ul>
{data.allMarkdownRemark.edges.map((edge) => {
return (
<li key={edge.node.id}>
<h2>
<Link to={`/blog/${edge.node.fields.slug}/`}>
{edge.node.frontmatter.title}
</Link>
</h2>
<div>
<span>
Posted on {edge.node.frontmatter.date}
</span>
</div>
<p>{edge.node.excerpt}</p>
<div>
<Link to={`/blog/${edge.node.fields.slug}/`}>Read More</Link>
</div>
</li>
);
})}
</ul>
</Layout>
);
};
export default Blogs;
Lets say I have multiple blogs and I wish to show only a specific one in a page through query like...
query MyQuery {
markdownRemark((id: {eq: "9ac19d6d"}) //Some ID {
title
description
content
}
}
How to get this on a page to display?
Thanks in advance!
Depending on what do you want to achieve:
If you want just a specific single post. You can filter your useStaticQuery to add the value of the id (if you know it beforehand) like:
query MyQuery {
markdownRemark((id: {eq: "123"}) {
title
description
content
}
}
useStaticQuery as the name points out, is static and doesn't accept dynamic values.
Another alternative is to get a specific position from data.allMarkdownRemark to display it.
If you just want a single post without any filter you can take advantage of the GraphQL query options:
{
allMarkdownRemark(limit: 1) {
edges {
node {
frontmatter {
title
}
}
}
}
}
If you are trying to create dynamic posts, hence each post template will display a different blog post (one per template), you need to pass a filter value from gatsby-node.js (where you create the post pages) to the template through Gatsby's context:
// gatsby-node.js
posts.forEach(({ node }, index) => {
createPage({
path: node.fields.slug,
component: path.resolve(`./src/templates/blog-post.js`),
context: {
id: node.id,
title: node.title
},
})
})
Note: here I'm lifting the id and the title. Use whatever works better for you
Now, you can take advantage of the context in your Blogs component (as long as it's a template):
const Blogs = ({data}) => {
console.log("your blog post is:", data)
return (
<Layout>
<h1>{data.markdownRemark.title}</h1>
</Layout>
);
};
export const query = graphql`
query($id: String!, $title: String!) {
markdownRemark((id: {eq: $id}) {
title
description
content
}
}
`
export default Blogs;
In other words: the first approach uses a static query (via useStaticQuery hook. Static, no dynamic parameters allowed) and the second uses a page query (only available in pages or templates)
With your query:
query MyQuery {
markdownRemark((id: {eq: "9ac19d6d"}) //Some ID {
title
description
content
}
}
Your data will be in data.markdownRemark
You can access those 3 fields directly.
const { title, description, content ] = data.markdownRemark;
return (
<Layout>
<div>
<p>{title}</p>
<p>{description]</p>
<p>{content}</p>
</div>
</Layout>
)
Related
I'm building a NextJS application and struggle to pass data into dynamically generated pages.
In my application, data is fetched from an Amazon S3 bucket and mapped. The fetch works as intended delivering a set of tiles populated with data from the external source.
{data
?.filter(
(item: any) =>
selectedCategory === null ||
item.videoCategory === selectedCategory
)
.map((item: any) => (
<div key={item.id}>
{showOverlay && (
<div className="relative inset-0 flex h-80 w-80 animate-pulse items-center justify-center bg-red-500 text-center"></div>
)}
<Link
href="/videos/normal/[video]"
as={`/videos/normal/${item.videoID}`}
>
<HoverVideoPlayer videoSrc={item.videoLink} />
</Link>
</div>
))}
Now, I want every tile to link to a dynamically generated detail page, for this I have generated a slugged [video].tsx page.
The data on each dynamically generated page should be based on the 'videoID' property of the data / array it was mapped from. The data looks like this:
"id": "2",
"videoLink": "https://media.w3.org/2010/05/sintel/trailer_hd.mp4",
"videoLinkDeep": "https://media.w3.org/2010/05/sintel/trailer_hd.mp4",
"videoTitle": "Stopmotion",
"videoID": "100007_LX",
Linking to the page works, the page component is rendered correctly, but as far as I can tell - no data whatsoever is passed.
My [video].tsx is setup like this:
import React from 'react';
const VideoPage = (props: {
params: VideoParams;
json: any;
video: any;
data: any;
}) => {
if (!props.video) {
return <p>Video not found</p>;
}
return (
<div>
<h1>{props.video.videoTitle}</h1>
</div>
);
};
interface VideoParams {
id: string;
}
export async function getServerSideProps({ params }: { params: VideoParams }) {
const res = await fetch('https://www.externalsource.com/VideoData.json');
const json = await res.json();
console.log(params.id);
console.log(json);
const video = json.find((item: any) => item.link === params.id);
console.log(video);
if (video) {
return {
props: {
video,
},
};
}
return {
props: {
video: null,
},
};
}
export default VideoPage;
I have checked all spelling / import related issues and integrated a Fallbacks and multiple console logs to debug:
This fallback is triggered: delivering a 'Video not Found' when opening one of the dynamic pages
if (!props.video) {
return Video not found;
}
My console.log for the
console.log(video);
tells me that all data from the external source is fetched, but closes with an 'undefined'.
What is going wrong in the handover between the Link and the dynamic page?
When you define a dynamic route in NextJS, the name of the file will be used as the param in params. You're trying to access params.id, but the file is named [video].tsx, so you should be accessing params.video.
Here's the documentation on dynamic routes: https://nextjs.org/docs/routing/dynamic-routes
I want to turn my Wordpress Blog into a react app through Hygraph headless CMS.
My API query has "wordPressPosts" posts within a master "posts" property.
const { posts } = await hygraph.request(
`
{
posts {
wordPressPosts {
id
title {
rendered
}
categories
}
}
}
`
);
How can I access wordPressPosts "ids" and post "titles" to map them? I tried messing with posts.wordPressPosts but it does not display anything on screen.
return {
props: {
posts
}
};
}
export default ({ posts }) =>
posts.map(({ id, title }) => <p key={id}>{title}</p>);
Full code sandbox: https://codesandbox.io/s/wp-source-for-hygraph-li2ssl?file=/src/pages/index.js
Cheers,
L.
I'm working on the blog based on React, TS and Gatsby.
Blog posts are based on markdown. Each blog post will have a similar header with title, time necessary to read the article and the logos of the technologies that the particular blog post will be about.
I have a problem with rendering those images dynamically. My idea was to create something like this in markdown
---
path: "/fourth"
date: "2021-06-02"
title: "TypeScript - intro"
readTime: "140"
author: "Adam Kniec"
imgs: [typescript, react]
---
That's the fourth blog post
after that I wanted to create a graphql query and get the imgs names like so:
export const postQuery = graphql`
query BlogPostByPath($path: String!) {
markdownRemark(frontmatter: { path: { eq: $path } }) {
html
frontmatter {
path
readTime
title
author
imgs
date
}
}
}
`;
I have the array of images in the props now and I wanted to render those images like this
{data.markdownRemark.frontmatter.imgs.map((imgPath) => {
const imgPatha = `../images/${imgPath}`;
return <StaticImage src={imgPatha} alt="asdasd" />;
})}
Unfortunately, gatsby gives me the warning
react_devtools_backend.js:2560 Image not loaded ../images/typescript
Is this the correct approach ? Please let me know what I'm doing wrong or how to render those images dynamically.
As it has been said by #coreyward you can't use dynamic props in the StaticImage component, it's a known restriction.
That said, you have two choices:
Using the standard img HTML tag.
Using the GatsbyImage component. To do it, you'll need to add the images in your filesystem to allow Gatsby to create the proper nodes and then, you will need to query them in your pages/templates. Without further details on the implementation, it's impossible to guess how your code should look like but the idea relies on something like:
import { graphql } from "gatsby"
import { GatsbyImage, getImage } from "gatsby-plugin-image"
function BlogPost({ data }) {
const image = getImage(data.blogPost.avatar)
return (
<section>
<h2>{data.blogPost.title}</h2>
<GatsbyImage image={image} alt={data.blogPost.author} />
<p>{data.blogPost.body}</p>
</section>
)
}
export const pageQuery = graphql`
query {
blogPost(id: { eq: $Id }) {
title
body
author
avatar {
childImageSharp {
gatsbyImageData(
width: 200
placeholder: BLURRED
formats: [AUTO, WEBP, AVIF]
)
}
}
}
}
`
I am currently working on a gatsby website and am mostly using react components doing so. The way our jobs posting page currently works is that it fetches a list of jobs from an array that stores the information for each position and maps them out with proper stylization.
positions.js (section)
const Positions = () => (
<StyledPositions>
<GlobalContainer>
{PositionList.map((position, index) => (
<StyledPosition key={index}>
<StyledPositionName>
<Link to={position.link} activeClassName="active">
{position.name}
</Link>
</StyledPositionName>
</StyledPosition>
))}
</GlobalContainer>
</StyledPositions>
);
export default Positions;
PositionList is the array and it looks like this.
positionslist.js
const Positionlist = [
{
name: "Senior Software Engineer- Infrastructure",
link: "/careers/open_positions/sr_cloud_eng",
},
{
name: "System Software Engineer",
link: "/careers/open_positions/system_soft_eng",
},
{
name: "Software Engineer (Database Development)",
link: "/careers/open_positions/soft_eng_database",
},
];
export default Positionlist;
What I'm trying to do is to populate the job posting site from a GraphQL query from an external job posting management site. I am able to fetch the information fine, but I'd like to somehow turn the information into an array like positionslist.js so that positions.js can simply map the information the same way. The query looks like this
query MyQuery {
allLever {
edges {
node {
text
categories {
commitment
location
team
}
applyUrl
}
}
}
}
All GraphQL queries store their result inside props.data as you can see in Gatsby's tutorial. At this point, you only need to enter to the nested structure like:
import * as React from 'react'
import { graphql } from 'gatsby'
const Positions = ({data}) => {
let PositionList = data.edges;
return <StyledPositions>
<GlobalContainer>
{PositionList.map(({node: position}) => (
<StyledPosition key={position.name}>
<StyledPositionName>
<Link to={position.link} activeClassName="active">
{position.name}
</Link>
</StyledPositionName>
</StyledPosition>
))}
</GlobalContainer>
</StyledPositions>
}
export const query = graphql`
query MyQuery {
allLever {
edges {
node {
name: text
categories {
commitment
location
team
}
link: applyUrl
}
}
}
}
`
export default Positions
Note: it's better to avoid using index as key so you can use another field
Keep in mind that you barely need to change your previous structure (because of some destructuring + aliasing) but, what's important is that page queries, only work in pages (hence the name) so I'm assuming that Positions is a separate page. Otherwise, you will need to pass down the props from the page component.
Because of the aliasing, applyUrl will be aliased as link and text to name, so your previous structure will work exactly in the same way.
I am working on a Gatsby website, and I keep getting "TypeError: Cannot read property 'childImageFluid' of undefined"
The code I have is this in my Project.js file
import React from "react"
import PropTypes from "prop-types"
import Image from "gatsby-image"
import { FaGithubSquare, FaShareSquare } from "react-icons/fa"
const Project = ({description, title, github, stack, url, image, index}) => {
return (
<article className="project">
<Image fluid={image.childImageFluid.fluid} className="project-img" />
</article>
)
}
Project.propTypes = {}
export default Project
and I have the graphql set up in the index.js file where it will be displayed, and everything is working as it should in graphql...
export const query = graphql`
{
allStrapiProjects(filter: { featured: { eq: true } }) {
nodes {
github
id
description
title
url
image {
childImageSharp {
fluid {
...GatsbyImageSharpFluid
}
}
}
stack {
id
title
}
}
}
}
`
everything up to the what I am working on in the Project.js file is in my github - https://github.com/matthewbert86/gatsby-site but all of that code is in the first code section above.
When you use a page query in GraphQL, your gathered data is stored inside a data object (as a props). You need to iterate through it until you get your fluid image. It should be in: props.data.allStrapiProjects.nodes.image.childImageFluid.fluid. Since you are destructuring everything in your <Project> component:
const Project = ({ data }) => {
let { description, title, github, stack, url, image } = data.allStrapiProjects.nodes; // your data is here
return (
<article className="project">
<Img fluid={data.allStrapiProjects.nodes.image.childImageFluid.fluid} className="project-img" />
</article>
)
}
After destructuring, you can refactor it to:
<Img fluid={image.childImageFluid.fluid} className="project-img" />
Another point, I guess that the gatsby-image import should be Img, not Image.