I Cannot Load Images From TMDB API - reactjs

I am trying to build a HULU Clone
I have build header successfully and
I am Building a results section where user can see images of movie but when I load images it just does not load Images
Technology I am Using
React.Js
Next.js
Tailwind css
TMDB Api
The Error Says - Failed to load resource: the server responded with a status of 404 ()
I assume its an error due to poor request to the TMDB Api
Here is My request.js
const API_KEY = process.env.API_KEY;
export default {
fetchTrending: {
title: "Trending",
url: `/trending/all/week?api_key=${API_KEY}&language=en-US`,
},
fetchTopRated: {
title: "Top Rated",
url: `/movie/top_rated?api_key=${API_KEY}&language=en-US`,
},
fetchActionMovies: {
title: "Action",
url: `/discover/movie?api_key=${API_KEY}&with_genres=28`,
},
fetchComedyMovies: {
title: "Comedy",
url: `/discover/movie?api_key=${API_KEY}&with_genres=35`,
},
fetchHorrorMovies: {
title: "Horror",
url: `/discover/movie?api_key=${API_KEY}&with_genres=27`,
},
fetchRomanceMovies: {
title: "Romance",
url: `/discover/movie?api_key=${API_KEY}&with_genres=10749`,
},
fetchMystery: {
title: "Mystery",
url: `/discover/movie?api_key=${API_KEY}&with_genres=9648`,
},
fetchSciFi: {
title: "Sci Fi",
url: `/discover/movie?api_key=${API_KEY}&with_genres=878`,
},
fetchWestern: {
title: "Western",
url: `/discover/movie?api_key=${API_KEY}&with_genres=37`,
},
fetchAnimation: {
title: "Animation",
url: `/discover/movie?api_key=${API_KEY}&with_genres=16`,
},
fetchTV: {
title: "TV Movie",
url: `/discover/movie?api_key=${API_KEY}&with_genres=10770`,
},
};
And Here is My index.js
import Head from "next/head";
import Header from "../components/Header";
import Nav from "../components/Nav";
import Results from "../components/Results";
import request from "../utils/request";
export default function Home({ results }) {
return (
<div>
<Head>
<title>HULU</title>
<meta name="description" content="Generated by create next app" />
<link rel="icon" href="/favicon.ico" />
</Head>
<Header />
<Nav />
<Results results={results} />
</div>
);
}
export async function getServerSideProps(context) {
const genre = context.query.genre;
const requests = await fetch(
`https://api.themoviedb.org/3${
request[genre]?.url || request.fetchTrending.url
}`
).then((res) => res.json());
return {
props: {
results: requests.results,
},
};
}
and This is where I Load Images in Thumbnail.js
import React from "react";
import Image from "next/image";
function Thumbnail({ result }) {
const BASE_URL = "https://image.tmdb.org/t/p/orignal";
console.log(result);
return (
<div>
<Image
layout="responsive"
src={
`${BASE_URL}${result.backdrop_path || result.poster_path}` ||
` ${BASE_URL}${result.poster_path}`
}
height={1080}
width={1920}
/>
</div>
);
}
export default Thumbnail;
this is what results console logs in Thumbnail.js
Result Console Log
Error Shown in Console
Please Tell Me If You Need Any Further Information
Thank You in Advance

The URL that is throwing the error points to http://localhost:3000 instead of the IMBD URL that you have.
Ensure that your proxy is not prepending http://localhost:3000 to all HTTP requests
Also ensure that you are not using relative URLS as that will cause http://localhost:3000 to be prepended to the HTTP request.

I have faced same issue. Then solve by changing few code in next.config.js
file like below
module.exports = {
images: {
domains:['image.tmdb.org'],
},
}
instead of module.exports =nextConfig

Related

Next js seo not working when sharing link

Im currently trying to implement SEO for a next.js and react webside, im using next-seo library for this. But i have the problem that when i share the link of the different sections i alway get the default title and description.
if i inspect the page, the title, description and openGraph options are been change, but at the moment of sharing the link of the page / pages or sharing on social media using the library next-share the default config is always taken. Im using getStaticProps() for most of the pages and getServerSideProps() for the my post section.
_app.tsx
const clientSideEmotionCache = createEmotionCache();
interface CustomAppProps extends AppProps {
emotionCache?: EmotionCache;
}
function ApinityApp({ Component, emotionCache = clientSideEmotionCache, pageProps }: CustomAppProps) {
return (
<>
<DefaultSeo {...SEO} />
<ApinityHomeUtilTracking containerId={process.env.NEXT_PUBLIC_PIWIK_ID}>
<HideAndSeek>
<MainMenuProvider>
<CacheProvider value={emotionCache}>
<Head>
<meta name="viewport" content="initial-scale=1, width=device-width" />
</Head>
<ThemeProvider theme={theme}>
<SnackbarProvider>
<Layout>
<Component {...pageProps} />
</Layout>
</SnackbarProvider>
</ThemeProvider>
</CacheProvider>
</MainMenuProvider>
</HideAndSeek>
</ApinityHomeUtilTracking>
</>
);
}
export default ApinityApp;
Default config
const SEO = {
title: 'API Business Operations SaaS platform | apinity.io',
description:
'Our SaaS platform for API Business Operations enables you to get the most business value from your APIs and make them part of your future API strategy.',
noindex: false,
openGraph: {
type: 'website',
locale: 'en_DE',
url: 'https://apinity.io/',
title: 'API Business Operations SaaS platform | apinity.io',
description:
'Our SaaS platform for API Business Operations enables you to get the most business value from your APIs and make them part of your future API strategy.',
images: [
{
url: 'https://apinity.io/images/logos/Apinity-logo-white-text.svg',
width: 139,
height: 35,
alt: 'apinity GmbH logo',
},
],
site_name: 'apinity GmbH',
},
twitter: {
handle: '#handle',
site: '#site',
cardType: 'summary_large_image',
},
};
export default SEO;
About page
export function AboutUs({ cmsData, menu }: { cmsData: StrapiMarketingAboutUs; menu: MainMenuItem[] }) {
const { aboutUsHome, ourTeamDetail, apinityLeaders, ourApinityStory, apinityOurAwards, apinityInvestor, apinityPartners, apinityScaleUp, getInTouch, seo } =
cmsData.marketingAboutUs.data.attributes;
const { setMenuItems } = useMainMenu();
setMenuItems(menu);
return (
<>
<NextSeo {...seoConfigAdapter(seo)} />
seoConfigAdapter
export const seoConfigAdapter = (seo) => {
if (!seo) {
return {};
}
const { metaTitle, metaDescription, metaRobots, canonicalURL, keywords, metaImage } = seo;
return {
title: metaTitle,
description: metaDescription,
canonical: canonicalURL,
noindex: false,
openGraph: {
type: 'website',
locale: 'en_IE',
url: window.location.href,
title: metaTitle,
description: metaDescription,
images: metaImage
? [
{
url: metaImage.data.attributes.url,
width: metaImage.data.attributes.width,
height: metaImage.data.attributes.height,
alt: metaImage.data.attributes.name,
},
]
: [],
},
additionalMetaTags: [
{
name: 'keywords',
content: keywords,
},
{
name: 'googlebot',
content: metaRobots,
},
],
};
};

I want to upload images to S3 from react-admin using fastapi endpoint

I am creating a backend API with FastAPI. I want to use this endpoint to upload images to S3 from the admin screen I am creating with React-Admin and INSERT the image URL and other column values into the database.
If the POST is successful, the response will be as follows.
{
"product_name": "test",
"detail": "test",
"obj_url": "{ URL }"
}
The following is the error on the backend side when posting and the payload when posting with React-Admin.
// POST /products/ HTTP/1.1" 422 Unprocessable Entity
{
product_name: "test",
detail: "<p>test</p>",
file: {
rawFile: {
path: "test.jpg"
},
name: "test.jpg",
url: "blob:{ URL }"
}
}
// Shown as 422(response)
{
"detail": [
{
"loc": [
"query",
"product_name"
],
"msg": "field required",
"type": "value_error.missing"
},
{
"loc": [
"query",
"detail"
],
"msg": "field required",
"type": "value_error.missing"
}
]
}
I thought that the above error would occur because the API is nested when posting with react-admin, so I tried changing the dataprovider, but couldfast not get it to work.
If anyone has a solution, I would appreciate your help.
Sorry if my weird English is hard to understand.
FastAPI
# routers/post.py
#router.post("/")
def post_product(product_name: str, detail: str, file: UploadFile = File(None)):
AWS_DEFAULT_REGION = os.getenv("AWS_DEFAULT_REGION")
AWS_BUCKET_NAME = os.getenv("AWS_BUCKET_NAME")
s3_bucket = AWS_BUCKET_NAME
s3_dir = "test-img"
region_name = AWS_DEFAULT_REGION
s3 = boto3.client("s3", region_name=region_name)
response = s3.put_object(
Body=file.file, Bucket=s3_bucket, Key=f"{s3_dir}/{file.filename}"
)
file_url = "https://%s.s3.%s.amazonaws.com/%s" % (
s3_bucket,
region_name,
s3_dir + "/" + file.filename,
)
insert = {"product_name": product_name, "detail": detail, "obj_url": file_url}
conn = psycopg2.connect(
dbname="postgres",
user="postgres",
password="Password",
host="localhost",
port=5432,
)
cur = conn.cursor()
cur.execute(
"INSERT INTO products (product_name, detail, obj_url) VALUES (%s, %s, %s)",
(insert["product_name"], insert["detail"], insert["obj_url"]),
)
conn.commit()
response = {"product_name": product_name, "detail": detail, "obj_url": file_url}
return response
React-Admin
// App.tsx
import React from "react";
import { Admin, fetchUtils, Resource } from "react-admin";
import { PostList, PostEdit, PostCreate } from "./components/Posts";
import { ProfileList, ProfileEdit } from "./components/Profiles";
import { ProductList, ProductEdit, ProductShow } from "./components/Products";
import { ProductCreate } from './components/Products'
import jsonServerProvider from "ra-data-json-server";
import simpleRestProvider from "ra-data-simple-rest";
import "./App.css";
const dataProvider = jsonServerProvider("http://localhost:8000", fetchUtils.fetchJson);
function App() {
return (
<Admin dataProvider={dataProvider}>
<Resource name="posts" list={PostList} edit={PostEdit} create={PostCreate} />
<Resource name="profiles" list={ProfileList} edit={ProfileEdit} />
<Resource name="products" list={ProductList} edit={ProductEdit} create={ProductCreate} show={ProductShow} />
</Admin>
);
}
export default App;
// in src/components/Posts/index.tsx
import * as React from "react";
import {
List,
Show,
Edit,
Create,
Datagrid,
TextField,
NumberField,
ImageField,
SimpleForm,
TextInput,
NumberInput,
ImageInput,
required,
SimpleShowLayout
} from "react-admin";
import { RichTextInput } from 'ra-input-rich-text';
export const ProductCreate = () => (
<Create>
<SimpleForm>
<TextInput source="product_name" validate={[required()]} fullWidth />
<RichTextInput source="detail" />
<ImageInput
// multiple={true}
source="file"
label="Related pictures"
accept="image/*"
>
<ImageField source="url" title="name" />
</ImageInput>
</SimpleForm>
</Create>
);
Data Provider(Some parts are omitted.)
// index.ts
// https://github.com/typicode/json-server
export default (apiUrl, httpClient = fetchUtils.fetchJson): DataProvider => ({
getList: (resource, params) => {
const { page, perPage } = params.pagination;
const { field, order } = params.sort;
const query = {
...fetchUtils.flattenObject(params.filter),
_sort: field,
_order: order,
_start: (page - 1) * perPage,
_end: page * perPage,
};
const url = `${apiUrl}/${resource}?${stringify(query)}`;
return httpClient(url).then(({ headers, json }) => {
if (!headers.has('x-total-count')) {
throw new Error(
'The X-Total-Count header is missing in the HTTP Response. The jsonServer Data Provider expects responses for lists of resources to contain this header with the total number of results to build the pagination. If you are using CORS, did you declare X-Total-Count in the Access-Control-Expose-Headers header?'
);
}
return {
data: json,
total: parseInt(
headers.get('x-total-count').split('/').pop(),
10
),
};
});
},
create: (resource, params) =>
httpClient(`${apiUrl}/${resource}`, {
method: 'POST',
body: JSON.stringify(params.data),
}).then(({ json }) => ({
data: { ...params.data, id: json.id },
})),
});

(Gatsby) How to pass image source as a prop in MDX component

I'm trying to create a Figure component in which I pass the img src along with other data.
I realize it's not straightforward—e.g., this thread—but I thought it would work fine with normal HTML img tags.
The image is not displaying. Does that mean that this limitation also applies to HTML img tags within components?
If this is the case, I guess I indeed ought to use Gatsby's dynamic images. How would I do this in a static query (nonpage component)? This thread had me believing it isn't possible—or at least a hack?
The component inside MDX documents:
<Figure
image=""
size=""
caption=""
credits=""
/>
Figure.js:
import * as React from "react"
const Figure = ({ image, size, caption, credits }) => {
return (
<figure className={size}>
<img src={image} alt={caption} />
<figcaption>
<span>{caption}</span>
<span>{credits}</span>
</figcaption>
</figure>
)
}
export default Figure
articlePostTemplate.js
import * as React from "react"
import { graphql } from "gatsby"
import { MDXRenderer } from "gatsby-plugin-mdx"
import Layout from "../components/layout.js"
import Seo from "../components/seo.js"
const PostTemplate = ({ data, location }) => {
let post = data.mdx
return (
<Layout location={location}>
<Seo
title={post.frontmatter.title}
description={post.frontmatter.lead}
date={post.frontmatter.computerDate}
/>
<article className="post">
<header className="post" id="intro">
<p className="date">
<time dateTime={post.frontmatter.computerDate}>{post.frontmatter.humanDate}</time>
</p>
<h1 itemprop="headline">{post.frontmatter.title}</h1>
<p className="lead">{post.frontmatter.lead}</p>
</header>
<section className="post" id="body-text">
<MDXRenderer data={data}>{post.body}</MDXRenderer>
</section>
</article>
</Layout>
)
}
export default PostTemplate
export const pageQuery = graphql`
query PostBySlug(
$id: String!
) {
site {
siteMetadata {
title
}
}
mdx(id: { eq: $id }) {
id
excerpt(pruneLength: 160)
body
frontmatter {
title
computerDate: date(formatString: "YYYY-MM-DD")
humanDate: date(formatString: "D. MMMM YYYY", locale: "nb")
hook
type
lead
featuredImage {
childImageSharp {
fluid(maxWidth: 800) {
...GatsbyImageSharpFluid
}
}
}
}
}
}
`
gatsby-config.js
module.exports = {
…
},
plugins: [
`gatsby-plugin-image`,
`gatsby-plugin-sitemap`,
{
resolve: `gatsby-plugin-mdx`,
options: {
extensions: [`.md`, `.mdx`],
gatsbyRemarkPlugins: [
{
resolve: `gatsby-remark-images`,
options: {
maxWidth: `900000000000`,
linkImagesToOriginal: false,
backgroundColor: `none`,
},
},
{
resolve: `gatsby-remark-responsive-iframe`,
options: {
wrapperStyle: `margin-bottom: 1.07var(--line-length)`,
},
},
`gatsby-remark-prismjs`,
`gatsby-remark-copy-linked-files`,
`gatsby-remark-smartypants`,
{
resolve: `gatsby-remark-autolink-headers`,
options: {
icon: false,
itemprop: `heading`,
maintainCase: false,
removeAccents: true,
elements: [`h2`, `h3`, `h4`],
},
}
],
},
},
…
{
resolve: `gatsby-source-filesystem`,
options: {
name: `content`,
path: `${__dirname}/content/`,
}
},
{
resolve: `gatsby-source-filesystem`,
options: {
path: `${__dirname}/src/data`,
},
},
{
resolve: `gatsby-source-filesystem`,
options: {
path: `${__dirname}/src/images`,
},
},
{
resolve: `gatsby-source-filesystem`,
options: {
path: `${__dirname}/src/pages/`,
}
},
{
resolve: `gatsby-source-filesystem`,
options: {
name: `journalistikk`,
path: `${__dirname}/content/journalism/`,
}
},
{
resolve: `gatsby-source-filesystem`,
options: {
name: `discussion`,
path: `${__dirname}/content/discussion/`,
}
},
{
resolve: `gatsby-source-filesystem`,
options: {
name: `photography`,
path: `${__dirname}/content/photography/`,
}
},
`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`,
],
}
gatsby-node.js
const path = require(`path`)
const { createFilePath } = require(`gatsby-source-filesystem`)
exports.createPages = async ({ graphql, actions, reporter }) => {
const { createPage } = actions
const articlePostTemplate = path.resolve(`./src/templates/articlePostTemplate.js`)
const result = await graphql(
`
{
allMdx(
sort: { fields: [frontmatter___date], order: ASC }
limit: 1000
) {
nodes {
id
frontmatter {
title
computerDate: date(formatString: "YYYY-MM-DD")
humanDate: date(formatString: "D. MMMM YYYY", locale: "nb")
}
fields {
slug
}
}
}
}
`
)
if (result.errors) {
reporter.panicOnBuild(
`There was an error loading your blog posts`,
result.errors
)
return
}
const posts = result.data.allMdx.nodes
if (posts.length > 0) {
posts.forEach((post, index) => {
[index + 1].id
createPage({
path: post.fields.slug,
component: articlePostTemplate,
context: {
id: post.id
},
})
})
}
}
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
}
`)
}
layout.js
import * as React from "react"
import { MDXProvider } from "#mdx-js/react"
import { Link } from "gatsby"
import DWChart from "react-datawrapper-chart"
import Header from "./Header"
import Footer from "./Footer"
import Figure from "./Figure"
const shortcodes = { Link, DWChart, Figure }
export default function Layout({ children }) {
return (
<div className="layout-wrapper">
<Header />
<main>
<MDXProvider components={shortcodes}>{children}</MDXProvider>
</main>
<Footer />
</div>
)
}
The limitation you mention in How to pass a path of image as a prop in Gatsby in Gatsby-plugin-image only applies to StaticImage + dynamic props data (with a dynamic source), meaning that the component that returns a StaticImage cannot receive the src as a props, because all the props of StaticImage needs to be statically analyzed.
In other words, your MDX can receive a src to be used in a <img> tag or you can use GatsbyImage component if properly configured.
Keep in mind that the query that will fetch GatsbyImage data (childImageSharp, gatsbyImageData, etc) must be placed in a top-level component (pages) if using a page query or in a useStaticQuery hook if used elsewhere.
The approach in both scenarios (using <img> or GatsbyImage) is similar. You need to:
Provide your image sources in your markdown file
Query those images using a page query or useStaticQuery to provide to your MDXProvider the queried image props. Using the blog example:
<MDXProvider>
<MDXRenderer data={data}/>
</MDXProvider>
data stands for your queried data in a GraphQL page query or useStaticQuery. Without knowing your data structure I haven't add the GraphQL part because I do not know the nodes available.
At this point, your MDXProvider holds the images data (all from your markdown files), you just need to provide it to your GatsbyImage or Figure component:
// some.mdx
import Figure from 'path/to/figure/component';
## Post Body
Lorem ipsum dolor...
<Figure img={props.data} />
As you can see, I lift all props to Figure just to allow to debug (using console.logs() for example) to see the available props there like:
const Figure = (props) => {
console.log(props);
return (
<div>I will be a figure in a future</div>
)
}
export default Figure
In that way, you will be able to pass to Figure something like img={props.someNode.frontmatter.imageNodeSource}.
Again, without knowing your data structure is like a pie in the sky but get the idea.
Hat tip to Ferran for helpful guidance.
After more research, I revised my solution—
articleTemplate.js
/* shortcodes */
const ArticleTemplate = ({ data, location }) => {
let post = data.mdx
return (
<Layout location={location}>
<Seo
title={post.frontmatter.title}
description={post.frontmatter.lead}
date={post.frontmatter.computerDate}
/>
<article className="article">
<p className="date">
<time dateTime={post.frontmatter.computerDate}>{post.frontmatter.humanDate}</time>
</p>
<h1 itemprop="headline">{post.frontmatter.title}</h1>
<p className="lead" itemprop="introduction">{post.frontmatter.lead}</p>
<MDXProvider components={shortcodes}>
<MDXRenderer
data={post.frontmatter.thumbnail}
localImages={post.frontmatter.embeddedImagesLocal}
>
{post.body}
</MDXRenderer>
</MDXProvider>
</article>
</Layout>
)
}
export default ArticleTemplate
export const pageQuery = graphql`
query ArticleBySlug($id: String!) {
site {
siteMetadata {
title
}
}
mdx(id: {eq: $id}) {
id
excerpt(pruneLength: 160)
body
frontmatter {
title
computerDate: date(formatString: "YYYY-MM-DD")
humanDate: date(formatString: "D. MMMM YYYY", locale: "nb")
hook
type
lead
thumbnail {
childImageSharp {
gatsbyImageData(
layout: FULL_WIDTH
)
}
}
embeddedImagesLocal {
childImageSharp {
gatsbyImageData
}
}
}
}
}
`
figure.js
import * as React from "react"
import { GatsbyImage, getImage } from 'gatsby-plugin-image'
const Figure = ({ source, size, caption, credit }) => {
return (
<figure className={size}>
<GatsbyImage image={getImage(source)} alt={caption} />
<figcaption>
<span>{caption}</span>
<span>{credit}</span>
</figcaption>
</figure>
);
}
export default Figure
index.mdx
---
…
thumbnail: "thumb.jpeg"
embeddedImagesLocal:
- "first.jpeg"
- "second.jpeg"
---
<Figure
source={(props.localImages [0])} <!-- first image; second image would be `[1]` -->
size="I'm a `className`"
caption="I'm a caption"
photographer="I'm a name."
/>
(Marking this as the solution as it's the most helpful for anyone looking to do this in the future. It also shows how to query for embedded images and featured images—at the same time.)

How do I fix "TypeError: childImageSharp is undefined" on NetlifyCMS build?

I'm setting up a simple blog page on an existing gatsby site, I want the person using netlify cms to be able to upload a thumbnail image for the blog post. I've managed to do so and locate it using graphigl
Now I would like to set it to my blog posts page:
import React from "react"
import { Link, graphql, useStaticQuery } from "gatsby"
import Layout from "../components/layout/layout"
const Blog = () => {
const data = useStaticQuery(graphql`
query {
allMarkdownRemark {
edges {
node {
frontmatter {
title
date
thumbnail {
childImageSharp {
fluid(maxWidth: 400) {
src
}
}
}
}
fields {
slug
}
}
}
}
}
`)
return (
<>
<Layout>
<main className="main">
<div className="articles">
<h1 className="articles__title">Articles</h1>
{data.allMarkdownRemark.edges.map(edge => {
return (
<section className="articles__list">
<a className="articles__article">
<div className="articles__article-artwork">
<figure className="articles__article-artwork-wrapper">
{edge.node.frontmatter.thumbnail.childSharpImage.fluid.src}
</figure>
</div>
<h2 className="articles__article-title">
<Link to={`/blog/${edge.node.fields.slug}`}>
{edge.node.frontmatter.title}
</Link>
</h2>
<Link>
<p>{edge.node.frontmatter.date}</p>
</Link>
<div className="articles__article-description">
<p></p>
</div>
<span className="articles__article-more">Read more...</span>
</a>
</section>
)
})}
</div>
</main>
</Layout>
</>
)
}
export default Blog
Then I get these errors, when redeployed on netlify.
config.yml
backend:
name: github
branch: development
repo: (removed for work reasons)
media_folder: static/img
public_folder: img
collections:
- name: "blog"
label: "Blog"
folder: "src/posts"
create: true
slug: "{{slug}}"
fields:
- {label: "Layout", name: "layout", widget: "hidden", default: "blog"}
- {label: "Title", name: "title", widget: "string"}
- {label: "Publish Date", name: "date", widget: "datetime"}
- {label: "Body", name: "body", widget: "markdown"}
- {label: "Image", name: "thumbnail", widget: "image"}
gatsby-node.js
const path = require('path')
module.exports.onCreateNode = ({ node, actions }) => {
const { createNodeField } = actions
if (node.internal.type === "MarkdownRemark") {
const slug = path.basename(node.fileAbsolutePath, '.md')
createNodeField({
node,
name: 'slug',
value: slug
})
}
}
module.exports.createPages = async ({ graphql, actions}) => {
const { createPage } = actions
const blogTemplate = path.resolve('./src/templates/blog.js')
const res = await graphql(`
query {
allMarkdownRemark {
edges {
node {
fields {
slug
}
}
}
}
}
`)
res.data.allMarkdownRemark.edges.forEach((edge) => {
createPage({
component: blogTemplate,
path: `/blog/${edge.node.fields.slug}`,
context: {
slug: edge.node.fields.slug
}
})
})
}
gatsby-config.js
module.exports = {
siteMetadata: {
title: `removed for work reasons`,
description: `removed`,
author: `removed`,
},
plugins: [
`gatsby-plugin-react-helmet`,
`gatsby-plugin-sass`,
`gatsby-plugin-remove-serviceworker`,
`gatsby-transformer-sharp`,
`gatsby-plugin-sharp`,
{
resolve: `gatsby-source-filesystem`,
options: {
name: `images`,
path: `${__dirname}/src/images`,
},
},
{
resolve: `gatsby-source-filesystem`,
options: {
name: `img`,
path: `${__dirname}/static/img`,
},
},
{
resolve: `gatsby-source-filesystem`,
options: {
name: `src`,
path: `${__dirname}/src`,
},
},
{
resolve: `gatsby-plugin-manifest`,
options: {
name: `gatsby-starter-default`,
short_name: `starter`,
start_url: `/`,
background_color: `#663399`,
theme_color: `#663399`,
display: `minimal-ui`,
},
},
{
resolve: 'gatsby-plugin-react-svg',
options: {
rule: {
include: /assets/
}
}
},
{
resolve: `gatsby-transformer-remark`,
options: {
plugins: [
{
resolve: `gatsby-remark-images`,
options: {
maxWidth: 590,
}
},
{
resolve: `gatsby-plugin-netlify-cms-paths`,
options: {
cmsConfig: `/static/admin/config.yml`
}
}
]
}
},
// this (optional) plugin enables Progressive Web App + Offline functionality
// To learn more, visit: https://gatsby.dev/offline
// `gatsby-plugin-offline`,
`gatsby-plugin-netlify-cms`,
`gatsby-plugin-netlify`,
],
}
I honestly thought that adding a blog page to my existing gatsby site using netlify cms would be a breeze but it has been one of the most difficult things I've attempted.
Any help is very much appreciated.
Thanks
There are a few things odd to me.
Your query is not working (so, breaking the code) because it can't find your image. Change your config.yml media paths to:
media_folder: static/img
public_folder: /img
Note the slash (/) in public_folder path.
This is because it's a relative path and must start with a slash. From Netlify docs (bolded the slash part):
Public Folder
This setting is required.
The public_folder option specifies the folder path where the files
uploaded by the media library will be accessed, relative to the base
of the built site. For fields controlled by [file] or [image] widgets,
the value of the field is generated by prepending this path to the
filename of the selected file. Defaults to the value of media_folder,
with an opening / if one is not already included.
public_folder: "/images/uploads"
Based on the settings above, if a user used an image widget field
called avatar to upload and select an image called philosoraptor.png,
the image would be saved to the repository at
/static/img/philosoraptor.png, and the avatar field for the
file would be set to /img/philosoraptor.png.
Your media_folder looks good.
The way you are rendering the image inside the <figure> tag. Following your approach, it will render a string with the path of the image, however, you are using the sharp from gatsby-image but you are not using it. I would recommend, among a few trials, the following:
<figure>
<Img fluid={edges.node.frontmatter.thumbnail.childImageSharp.fluid}>
</figure>
Following the gatsby-image approach, you should also use a query fragment like:
const data = useStaticQuery(graphql`
query {
allMarkdownRemark {
edges {
node {
frontmatter {
title
date
thumbnail {
childImageSharp {
fluid(maxWidth: 400) {
...GatsbyImageSharpFluid
}
}
}
}
fields {
slug
}
}
}
}
}
`)
Note the ...GatsbyImageSharpFluid fragment to fetch all needed data to use gatsby-image.
You are using a staticQuery but you don't need it, since all your data comes from a CMS. You should use a page/template query to improve the performance but this will change your page structure.
The "standard" way of creating dynamic pages in Gatsby is, using gatsby-node.js to use createPage API, pass the needed data to the template (normally an id or slug) and use that unique data to retrieve the blog/post information.
You are passing the slug via context but you are never using it:
context: {
slug: edge.node.fields.slug
}
In addition, you are loopìng through all articles with the static query again (allMarkdownRemark) what doesn't make sense and it's a waste of time and performance.
Your Blog template should look like:
import React from 'react'
import { graphql } from 'gatsby'
const Blog = ({data}) => {
return (
<div>
Blog title is: {data.markdownRemark.frontmatter.title}
</div>
)
}
export const query = graphql`
query BlogQuery($slug: String!) {
query {
markdownRemark(fields: { slug: { eq: $slug }}) {
html
frontmatter {
title
date
thumbnail {
childImageSharp {
fluid(maxWidth: 400) {
...GatsbyImageSharpFluid
}
}
}
}
fields {
slug
}
}
}
}
`
export default Blog
Note that you are passing the slug ($slug) as a required parameter (String!), so it can't be null to a page query. Afterward, you are filtering the markdown nodes (markdownRemark) to get the one that matches the context that you are passing in the gatsby-node.js file. In other words, in that context, you have the data for each post.
Also notice that you may need to change the query in order to match your data structure, I've posted it from scratch without knowing your fields. Use the localhost:8000/___graphql (GraphQL playground) to check it. Your fragments won't work in there since it's a limitation of the GraphQL but it will work on your code, so avoid its usage there but keep it in your code.

TypeError: Cannot read property 'prismic' of undefined

I'm working on a Project using Gatsby/GraphQL/Prismic.
When I'm trying to get data from Prismic API using GraphQL this error is popping up.
However when I query request on GraphiQL browser it's getting data from API.
But when use it inside Component it's throwing error.
Here's my partners.js component
import React, { Component } from 'react';
import { graphql } from 'gatsby';
import Swiper from 'react-id-swiper';
export const query = graphql`
{
prismic {
allPartners {
edges {
node {
name
description
image
_linkType
}
}
}
}
}
`;
export default class Partners extends Component {
state = {
partners: this.props.data.prismic.allPartners.edges
};
render() {
console.log(this.state.partners);
const params = {
navigation: {
nextEl: '.swiper-button-next',
prevEl: '.swiper-button-prev'
}
};
if (this.state.partners) {
return (
<div>
<h1>Partners</h1>
<Swiper {...params}>
{this.state.partners.map((partner) => {
return (
<div>
<img src={partner.node.title[0].text} />
</div>
);
})}
</Swiper>
</div>
);
}
return (
<div>
<h1>No partners</h1>
</div>
);
}
}
And here's my gatsby-config.js
require('dotenv').config({
path: `.env`
});
module.exports = {
siteMetadata: {
title: `Gatsby Default Starter`,
description: `Kick off your next, great Gatsby project with this default starter. This barebones starter ships with the main Gatsby configuration files you might need.`,
author: `#gatsbyjs`
},
plugins: [
`gatsby-plugin-react-helmet`,
{
resolve: `gatsby-source-filesystem`,
options: {
name: `images`,
path: `${__dirname}/src/images`
}
},
`gatsby-transformer-sharp`,
`gatsby-plugin-sharp`,
{
resolve: `gatsby-plugin-manifest`,
options: {
name: `gatsby-starter-default`,
short_name: `starter`,
start_url: `/`,
background_color: `#663399`,
theme_color: `#663399`,
display: `minimal-ui`,
icon: `src/images/gatsby-icon.png` // This path is relative to the root of the site.
}
},
{
resolve: `gatsby-source-prismic-graphql`,
options: {
repositoryName: process.env.PRISMIC_REPOSITORY_NAME,
accessToken: process.env.PRISMIC_ACCESS_TOKEN
}
}
]
};
I couldn't find solution to my question from gatsby-source-prismic-graphql's github page.
Does anybody faced the same issue?
There are a few things to note here.
Why aren't you creating any pages yet in your gatsby-config?
i think the syntax is not correct cause how is the class going to recieve the props and query. I'm not sure creating a react Class is that necessary
Add a return null before trying to retrieve the data of the query
The error you're getting its outdated, run gatsby clean and try again. In the error says that you're calling an incorrect value: allServs instead of allPartners.

Resources