Hello,
I am trying to implement different kind of structured data on dynamic pages such as blog post and pillar page which would contain FAQ.
I am using a method provided in the Next Js Documentation (Metadata - Rendering and Ranking | Learn Next.js) which is fine for small structured data such as organization and logo, but it is not for FAQ.
Indeed, the script is well displayed in the HTML code. However, it is creating multiples errors such as "Duplicate field "text" and custom type property which are not valid. (see screenshots attached)
Is there a way to smoothly implement data structured containing multiples paragraphs on Prismic's repeatable custom type/slices?
I have tried to add a preformatted Rich Text Field to the slice as suggested here, in vain.
Here the code for the FAQ slice:
import * as React from "react";
import Head from "next/head";
import { PrismicRichText } from "@prismicio/react";
import classes from "../../components/ui/Shared/Faq.module.css";
import { styled } from "@mui/material/styles";
import ArrowForwardIosSharpIcon from "@mui/icons-material/ArrowForwardIosSharp";
import MuiAccordion from "@mui/material/Accordion";
import MuiAccordionSummary from "@mui/material/AccordionSummary";
import MuiAccordionDetails from "@mui/material/AccordionDetails";
import Typography from "@mui/material/Typography";
import StructuredData from "../../components/seo/StructuredData";
import Script from "next/script";
/**
* @typedef {import("@prismicio/client").Content.FaqSlice} FaqSlice
* @typedef {import("@prismicio/react").SliceComponentProps<FaqSlice>} FaqProps
* @param { FaqProps }
*/
const Accordion = styled((props) => (
<MuiAccordion disableGutters elevation={0} square {...props} />
))(({ theme }) => ({
border: `1px solid ${theme.palette.divider}`,
"&:not(:last-child)": {
borderBottom: 0,
},
"&:before": {
display: "none",
},
}));
const AccordionSummary = styled((props) => (
<MuiAccordionSummary
expandIcon={<ArrowForwardIosSharpIcon sx={{ fontSize: "0.9rem" }} />}
{...props}
/>
))(({ theme }) => ({
backgroundColor:
theme.palette.mode === "dark"
? "rgba(255, 255, 255, .05)"
: "rgba(0, 0, 0, .03)",
flexDirection: "row-reverse",
"& .MuiAccordionSummary-expandIconWrapper.Mui-expanded": {
transform: "rotate(90deg)",
},
"& .MuiAccordionSummary-content": {
marginLeft: theme.spacing(1),
},
}));
const AccordionDetails = styled(MuiAccordionDetails)(({ theme }) => ({
padding: theme.spacing(2),
borderTop: "1px solid rgba(0, 0, 0, .125)",
}));
export default function CustomizedAccordions({ slice }) {
// const Faq = ({ slice }) => (
const [expanded, setExpanded] = React.useState("panel1");
const handleChange = (panel) => (event, newExpanded) => {
setExpanded(newExpanded ? panel : false);
};
const SliceStructuredData = {
"@context": "https://schema.org",
"@type": "FAQPage",
mainEntity: [
{
"@type": "Question",
name: slice.primary.question1,
acceptedAnswer: {
"@type": "Answer",
text: slice.primary.response_1,
},
},
{
"@type": "Question",
name: slice.primary.question_2,
acceptedAnswer: {
"@type": "Answer",
text: slice.primary.response_2,
},
},
{
"@type": "Question",
name: slice.primary.question_3,
acceptedAnswer: {
"@type": "Answer",
text: slice.primary.response_3,
},
},
{
"@type": "Question",
name: slice.primary.question_4,
acceptedAnswer: {
"@type": "Answer",
text: slice.primary.response_4,
},
},
{
"@type": "Question",
name: slice.primary.question_5,
acceptedAnswer: {
"@type": "Answer",
text: slice.primary.response_5,
},
},
{
"@type": "Question",
name: slice.primary.question_6,
acceptedAnswer: {
"@type": "Answer",
text: slice.primary.response_6,
},
},
],
};
return (
<React.Fragment>
<Head>
<script
type="application/ld+json"
dangerouslySetInnerHTML={{
__html: JSON.stringify(SliceStructuredData, null, "\t"),
}}
key="faq-jsonld"
/>
</Head>
<section className={classes["section-faq"]}>
<div className={classes["grid-faq"]}>
<PrismicRichText field={slice.primary.title} />
<div className={classes["box-faq"]}>
<div className={classes["box-text"]}>
{slice.items.map((item, i) => (
<PrismicRichText field={item.paragraph} key={i} />
))}
</div>
<div className={classes["box-accordion"]}>
<Accordion
expanded={expanded === "panel1"}
onChange={handleChange("panel1")}
className={classes["border-radius"]}
>
<AccordionSummary
aria-controls="panel1d-content"
id="panel1d-header"
className={classes["card-accordion"]}
>
<Typography
className={classes.question}
component={"span"}
variant={"body2"}
>
<PrismicRichText field={slice.primary.question1} />
</Typography>
</AccordionSummary>
<AccordionDetails>
<Typography
className={classes.answer}
component={"span"}
variant={"body2"}
>
<PrismicRichText field={slice.primary.response_1} />
</Typography>
</AccordionDetails>
</Accordion>
<Accordion
expanded={expanded === "panel2"}
onChange={handleChange("panel2")}
className={classes["border-radius"]}
>
<AccordionSummary
aria-controls="panel2d-content"
id="panel2d-header"
className={classes["card-accordion"]}
>
<Typography
className={classes.question}
component={"span"}
variant={"body2"}
>
<PrismicRichText field={slice.primary.question_2} />
</Typography>
</AccordionSummary>
<AccordionDetails>
<Typography
className={classes.answer}
component={"span"}
variant={"body2"}
>
<PrismicRichText field={slice.primary.response_2} />
</Typography>
</AccordionDetails>
</Accordion>
<Accordion
expanded={expanded === "panel3"}
onChange={handleChange("panel3")}
className={classes["border-radius"]}
>
<AccordionSummary
aria-controls="panel3d-content"
id="panel3d-header"
className={classes["card-accordion"]}
>
<Typography
className={classes.question}
component={"span"}
variant={"body2"}
>
<PrismicRichText field={slice.primary.question_3} />
</Typography>
</AccordionSummary>
<AccordionDetails>
<Typography
className={classes.answer}
component={"span"}
variant={"body2"}
>
<PrismicRichText field={slice.primary.response_3} />
</Typography>
</AccordionDetails>
</Accordion>
<Accordion
expanded={expanded === "panel4"}
onChange={handleChange("panel4")}
className={classes["border-radius"]}
>
<AccordionSummary
aria-controls="panel3d-content"
id="panel3d-header"
className={classes["card-accordion"]}
>
<Typography
className={classes.question}
component={"span"}
variant={"body2"}
>
<PrismicRichText field={slice.primary.question_4} />
</Typography>
</AccordionSummary>
<AccordionDetails>
<Typography
className={classes.answer}
component={"span"}
variant={"body2"}
>
<PrismicRichText field={slice.primary.response_4} />
</Typography>
</AccordionDetails>
</Accordion>
<Accordion
expanded={expanded === "panel5"}
onChange={handleChange("panel5")}
className={classes["border-radius"]}
>
<AccordionSummary
aria-controls="panel3d-content"
id="panel3d-header"
className={classes["card-accordion"]}
>
<Typography
className={classes.question}
component={"span"}
variant={"body2"}
>
<PrismicRichText field={slice.primary.question_5} />
</Typography>
</AccordionSummary>
<AccordionDetails>
<Typography
className={classes.answer}
component={"span"}
variant={"body2"}
>
<PrismicRichText field={slice.primary.response_5} />
</Typography>
</AccordionDetails>
</Accordion>
{slice.primary.question_6 && (
<Accordion
expanded={expanded === "panel6"}
onChange={handleChange("panel6")}
className={classes["border-radius"]}
>
<AccordionSummary
aria-controls="panel3d-content"
id="panel3d-header"
className={classes["card-accordion"]}
>
<Typography
className={classes.question}
component={"span"}
variant={"body2"}
>
<PrismicRichText field={slice.primary.question_6} />
</Typography>
</AccordionSummary>
<AccordionDetails>
<Typography
className={classes.answer}
component={"span"}
variant={"body2"}
>
<PrismicRichText field={slice.primary.response_6} />
</Typography>
</AccordionDetails>
</Accordion>
)}
{/* {slice.primary.question_7.empty && (
<Accordion
expanded={expanded === "panel7"}
onChange={handleChange("panel7")}
className={classes["border-radius"]}
>
<AccordionSummary
aria-controls="panel3d-content"
id="panel3d-header"
className={classes["card-accordion"]}
>
<Typography
className={classes.question}
component={"span"}
variant={"body2"}
>
<PrismicRichText field={slice.primary.question_7} />
</Typography>
</AccordionSummary>
<AccordionDetails>
<Typography
className={classes.answer}
component={"span"}
variant={"body2"}
>
<PrismicRichText field={slice.primary.response_7} />
</Typography>
</AccordionDetails>
</Accordion>
)}
{slice.primary.question_8.empty && (
<Accordion
expanded={expanded === "panel8"}
onChange={handleChange("panel8")}
className={classes["border-radius"]}
>
<AccordionSummary
aria-controls="panel3d-content"
id="panel3d-header"
className={classes["card-accordion"]}
>
<Typography
className={classes.question}
component={"span"}
variant={"body2"}
>
<PrismicRichText field={slice.primary.question_8} />
</Typography>
</AccordionSummary>
<AccordionDetails>
<Typography
className={classes.answer}
component={"span"}
variant={"body2"}
>
<PrismicRichText field={slice.primary.response_8} />
</Typography>
</AccordionDetails>
</Accordion>
)} */}
</div>
</div>
</div>
</section>
</React.Fragment>
);
}
// export default Faq;
Thank you for the help!