I'm building a simple blog with GatsbyJS using Contentful. I have a blogpage with the list of all posts with titles and dates. I want to add, just for the last post, the first lines of the article and the image used in the article.
...
import React from 'react'
import Layout from '../components/layout'
import { Link, graphql, useStaticQuery } from 'gatsby'
import { documentToReactComponents } from "#contentful/rich-text-react-renderer"
import blogStyles from './blog.module.scss'
import Head from '../components/head'
import TransitionLink from "gatsby-plugin-transition-link"
import AniLink from "gatsby-plugin-transition-link/AniLink"
const BlogPage = () => {
const data = useStaticQuery(graphql`
query{
allContentfulBlogPost (sort: { fields: publishedDate, order: DESC}){
edges{
node{
title
slug
publishedDate(formatString:"MMMM Do, YYYY")
}
}
}
}
`)
return (
<Layout>
<Head title = "Post" />
<h1>Post</h1>
<ol data-sal="slide-up" data-sal-delay="300" data-sal-easing="ease"
className={blogStyles.posts}>
{data.allContentfulBlogPost.edges.map((edge) => {
return (
<li className={blogStyles.post}>
<Link to={`/blog/${edge.node.slug}`}>
<h2>{edge.node.title}</h2>
<p>{edge.node.publishedDate }</p>
</Link>
</li>
)
})}
</ol>
</Layout>
)
}
export default BlogPage
Note: I've assumed you mean latest post, instead of last post.
You could use Aliases to split your query into two parts.
latestArticle has a limit: 1 and queries for the extra fields needed for the first lines of the article and the image used in the article (as well as title, slugExt, publishDate).
allOtherArticles has a skip: 1 and just queries for title, slugExt, publishDate.
const BlogPage = () => {
const { latestArticle, allOtherArticles } = useStaticQuery(graphql`
query MyQuery {
latestArticle: allContentfulBlogPost(sort: {order: DESC, fields: publishDate}, limit: 1) {
nodes {
title
slug
publishDate
excerpt {
excerpt
}
thumbnailImage {
imageFile {
id
fluid {
...GatsbyContentfulFluid
}
}
}
}
}
allOtherArticles: allContentfulBlogPost(sort: {order: DESC, fields: publishDate}, skip: 1) {
nodes {
title
slug
publishDate
}
}
}
`)
const latest = latestArticle.edges[0].node
return (
<Layout>
<ol data-sal="slide-up" data-sal-delay="300" data-sal-easing="ease" className={blogStyles.posts}>
<li className="latest-post">
<Link to={`/blog/${latest.slug}`}>
<h2>{latest.title}</h2>
<p>{latest.excerpt.excerpt}</p>
<Img fluid={latest.thumbnailImage.imageFile.fluid} />
</Link>
</li>
{allOtherArticles.edges.map((edge) => {
return (
<li className={blogStyles.post}>
<Link to={`/blog/${edge.node.slug}`}>
<h2>{edge.node.title}</h2>
<p>{edge.node.publishedDate }</p>
</Link>
</li>
)
})}
</ol>
</Layout>
)
}
export default BlogPage
The other option is to just query for all the fields you need. When you map over the data you can check the index and conditionally render the thumbnail and the excerpt (as well as the other stuff).
{data.allContentfulBlogPost.edges.map((edge, index) => {
const firstArticle = index === 0
return (
<li className={ blogStyles.post }>
<Link to={ `/blog/${edge.node.slug }`}>
{ firstArticle && (<Img fluid={edge.node.thumbnailImage.imageFile.fluid } />)}
<h2>{ edge.node.title }</h2>
{ firstArticle && (<p>{ edge.node.title }</p>)}
<p>{ edge.node.publishedDate }</p>
</Link>
</li>
)
})}
Related
I've been following this guide on how to render props from my sidebar to my title header based on this links:
Dynamic Navigation in Next.js
But it doesnt show up on my title component
Here is my current settings. Basically I wanted to change the title on my header based on the sub-url that were defined in my arrays
From sidebar.tsx
import { navItems } from "../../static/NavItems";
const Sidebar = () => {
const [activeIcon, setActiveIcon] = useState("");
const router = useRouter();
return (
<Wrapper>
<LogoContainer>
<Logo>
<Image src={logo} alt="Logo" width={200} />
</Logo>
</LogoContainer>
<NavItemsContainer>
{navItems.map((nav, i) => (
<NavItem key={i} onClick={() => setActiveIcon(nav.title)}>
<NavIcon style={{ color: nav.title === activeIcon && "#3773f5" }}>
{nav.icon}
</NavIcon>
<NavTitle>
<Link as={nav.as} href={nav.path}>
{nav.title}
</Link>
</NavTitle>
</NavItem>
))}
</NavItemsContainer>
</Wrapper>
);
};
from title component
import { useRouter } from "next/router";
const Title = ({ title }) => {
const router = useRouter();
return <div>{{router.pathname === "/" ? title : ""}</div>;
};
So in my header, I've just import the component and pass it as a props like this
import { Title } from '../title.tsx';
const Header = ({ title }) => {
return (
<Title title={title} />
)
is there anything that I missed here?
I'm pretty new here to gatsby/programming and i was playing around with the gatsby. I have below issue and I would appreciate if you could let me know where does it goes wrong? Below is my code.
Problem 1: Why i can't return/access the {post1.categories.nodes.name} ? It shows nothing on my page.
Note that there was no problem for me to access {post.video.videoSource} so i guess my Graphql worked fine but when i tried with {post1.categories.nodes.name}, it was literally blank.
Any suggestions? Thank you very much.
const BlogIndex = ({
data,
pageContext: { nextPagePath, previousPagePath },
}) => {
const posts = data.allWpPost.nodes
if (!posts.length) {
return (
<Layout isHomePage>
<Seo title="All posts" />
<Bio />
<p>
No blog posts found. Add posts to your WordPress site and they'll
appear here!
</p>
</Layout>
)
}
return (
<Layout isHomePage>
<Seo title="All posts" />
{/* <Bio /> */}
<Grid container rowSpacing={5} column spacing={5}>
<Grid item xs={12} sm={3} md={3} lg={3}>
<Paper>
<ol style={{ listStyle: `none` }}>
{posts.map(post1 => {
return <li>{post1.categories.nodes.name}</li>
})}
</ol>
</Paper>
</Grid>
<Grid item xs={12} sm={9} md={9} lg={9}>
<Paper>
<ol style={{ listStyle: `none` }}>
{posts.map(post => {
const hasVideoUrl = post.video.videoSource
if (hasVideoUrl !== null) {
return (
<li key={post.uri}>
<article
className="post-list-item"
itemScope
itemType="http://schema.org/Article"
>
<header>
<small>{post.video.videoSource}</small>
<small>{post.video.videoUrl}</small>
{/* <ReactPlayer url={post.video.videoUrl} /> */}
</header>
</article>
</li>
)
} else {
return null
}
})}
</ol>
</Paper>
</Grid>
</Grid>
{previousPagePath && (
<>
<Link to={previousPagePath}>Previous page</Link>
<br />
</>
)}
{nextPagePath && <Link to={nextPagePath}>Next page</Link>}
</Layout>
)
}
export default BlogIndex
export const pageQuery = graphql`
query WordPressPostArchive($offset: Int!, $postsPerPage: Int!) {
allWpPost(
sort: { fields: [date], order: DESC }
limit: $postsPerPage
skip: $offset
) {
nodes {
excerpt
uri
date(formatString: "MMMM DD, YYYY")
title
video {
videoSource
videoUrl
}
categories {
nodes {
name
}
}
}
}
}
`
Problem 2 [Added 2 July 2022]:
<ul>
{wpPosts.map(wpPost => {
wpPost.tags.nodes.map(tag => {
console.log(tag.name)
if (tag.name === "Blog") {
return (
<div>
<li>{tag.title}</li>
</div>
)
}
})
})}
</ul>
const data = useStaticQuery(
graphql`
query {
allWpPost(sort: { fields: date, order: DESC }, limit: 10) {
nodes {
excerpt
slug
uri
video {
videoTitle
videoUrl
}
title
date(formatString: "MMMM DD, YYYY")
tags {
nodes {
name
}
}
}
}
}
`
)
const wpPosts = data.allWpPost.nodes
Problem 1: Why i can't return/access the {post1.categories.nodes.name}
? It shows nothing on my page.
nodes (in post1.categories.nodes.name) is likely to be an array, so you will need to loop through it or access a specific position:
return <li>{post1.categories.nodes[0].name}</li>
Depending on your data structure you'll need to loop or just access to the first position. Without knowing your data it's difficult to guess.
I'm running into an issue where I've created a non page component with a StaticQuery that is pulling information with a set up like this.
const BestSellers = () => (
<div>
<StaticQuery
query={bestSellerQuery}
render={data => (
<div>
{data.allMarkdownRemark.edges.map(({ node }) => (
<Card className="m-2 index-card" key={node.id}>
<Link to={node.fields.slug}>
<GatsbyImage
className="card-img-top"
image={node.frontmatter.image}
alt={node.frontmatter.description}
/>
</Link>
<hr />
<CardBody>
<Link to={node.fields.slug}>
<CardTitle className="h4 text-light text-wrap">
{node.frontmatter.title}
</CardTitle>
</Link>
<CardSubtitle>{node.frontmatter.description}</CardSubtitle>
{/* <CardSubtitle>{node.excerpt}</CardSubtitle> */}
<CardSubtitle className="float-left mt-5">
Price: ${node.frontmatter.price}
</CardSubtitle>
<CardSubtitle>
<Badge color="danger float-right mt-5">
{node.frontmatter.tag}
</Badge>
</CardSubtitle>
</CardBody>
</Card>
))}
</div>
)}
/>
</div>
);
const bestSellerQuery = graphql`
query bestSellerQuery {
allMarkdownRemark(
filter: { frontmatter: { tag: { eq: "popular" }}}
sort: { fields: [frontmatter___date], order: DESC }
limit: 2
) {
edges {
node {
id
frontmatter {
title
description
price
tag
image {
childImageSharp {
gatsbyImageData(
layout: CONSTRAINED
height: 600
placeholder: BLURRED
formats: [AUTO, JPG]
transformOptions: { fit: COVER, cropFocus: ATTENTION }
)
}
}
}
fields {
slug
}
excerpt
}
}
}
}
`
export default BestSellers;
What I'm doing is my pages are being created programmatically from a Markdown file once it is clicked on so I'm trying to import this into the template that i use to create pages and it just shows loading(static query) I've tried using this same query in pages that do and don't have a page query on them and it results in the same as using this in the template component that I'm trying to use it in.
It was the issues in the graphql query which was being used more specifically the line
filter: { frontmatter: { tag: { eq: "popular" }}}
the issue was the "popular" in the MD file is in caps so by changing it to "POPULAR" that solved it....
Material that i Used
・Contentful
・Gatsby
・Graphql
Success
Got a Content from Contentful By using a 8000_graphql and show up on local URL(http://localhost:8000/)
Fail
Switched out a page URL(http://localhost:8000/) to
URL(http://localhost:8000/blog/....) that shows up 404 pages.
Error
warn The GraphQL query in the non-page component "C:/Users/taiga/Github/Gatsby-new-development-blog/my-blog/src/templates/blog.js" will not
Exported queries are only executed for Page components. It's possible you're
trying to create pages in your gatsby-node.js and that's failing for some
reason.
If the failing component(s) is a regular component and not intended to be a page
component, you generally want to use a <StaticQuery> (https://gatsbyjs.org/docs/static-query)
instead of exporting a page query.
blog.js
import React from 'react';
import { graphql } from 'gatsby';
import Layout from '../components/layout';
import Nav from '../components/nav';
import SEO from '../components/seo';
import './blog.css';
const BlogTemplate = (props) => {
return (
<Layout>
<SEO title={props.data.contentfulBlog.seoTitle} description={props.data.contentfulBlog.seoDescription} keywords={props.data.contentfulBlog.seoKeywords} />
<Nav />
<div className='blog__header'>
<div className='blog__hero' style={{backgroundImage: `url(${props.data.contentfulBlog.featuredImage.fluid.src})`}}></div>
<div className='blog__info'>
<h1 className='blog__title'>{props.data.contentfulBlog.title}</h1>
</div>
</div>
<div className='blog__wrapper'>
<div className='blog__content'>
<div dangerouslySetInnerHTML={
{__html: `${props.data.contentfulBlog.content.childMarkdownRemark.html}`}
} />
</div>
</div>
</Layout>
)
}
export default BlogTemplate;
export const query = graphql`
query BlogTemplate($id: String!) {
contentfulBlog(id: {eq: $id}) {
title
id
slug
content {
childMarkdownRemark {
html
}
}
seoTitle
seoDescription
seoAuthor
seoKeywords
seoImage {
fluid(maxWidth: 1200, quality: 100) {
...GatsbyContentfulFluid
src
}
}
featuredImage {
fluid(maxWidth: 1200, quality: 100) {
...GatsbyContentfulFluid
src
}
}
}
}
`
GOAL: use Gatsby to render Drupal 8 images associated with nodes
I can render the default Drupal 8 Article node info just fine using a GraphQL query. I cannot get the default image field to render (field_image) - it just renders the url of the image. So I'm almost there but definitely missing something fundamental. Help please?
import React from "react"
import { Link, graphql } from "gatsby"
import Img from "gatsby-image"
const BlogPage = ({data}) => (
<div>
<h1>Know What Grinds My Gears?</h1>
{ data.allNodeArticle.edges.map(
(
{ node }) => (
<div>
#the next Img line doesn't work (renders nothing)
<Img fluid={ node.relationships.field_image.uri.url } />
<h3> { node.title } </h3>
<div dangerouslySetInnerHTML={{ __html: node.body.summary }} />
<Link to= { node.id } >read</Link>
#the next <figure> line doesn't work (renders correct url to image file)
<figure> { node.relationships.field_image.uri.url }</figure>
</div>
)
)
}
</div>
)
export default BlogPage
export const query = graphql`
query allNodeArticle {
allNodeArticle {
edges {
node {
id
title
body {
value
format
processed
summary
}
relationships {
field_image {
uri {
value
url
}
}
}
}
}
}
}
`
what am I doing wrong?
The way we manage this is by having on the GraphQL of the node image:
field_image {
relationships {
field_media_image {
localFile {
publicURL
childImageSharp {
fluid(maxWidth: 520 maxHeight: 520, cropFocus: CENTER) {
...GatsbyImageSharpFluid
}
}
}
}
}
}
And then using it as
<Img
fluid={
data.nodePage.relationships.field_image.relationships.field_media_image.localFile.childImageSharp.fluid
}
/>