How can I render the children as a string in React 18/Next 13?
React says renderToString is not suggested on the client, and it's not clear to me how to render it on the server.
The documentation here gives an example but it's not clear how it works in an actual react component as I get errors that I cannot create another node if the previous one wasn't removed from the head.
import { createRoot } from 'react-dom/client';
import { flushSync } from 'react-dom';
const div = document.createElement('div');
const root = createRoot(div);
flushSync(() => {
root.render(<MyIcon />);
});
console.log(div.innerHTML); // For example, "<svg>...</svg>"
Source
Whether I get the data on the server or client side, just looking for a working example either or.
function ExampleChildComponent() {
return (
<div className="bg-green-500 w-20 h-20">
Hello I am a green box
<button className="bg-blue-100 px-6 py-3">I am a button</button>
</div>
)
}
function LogChild({ children }: any) {
// How do you get the children to a string?
console.log(someFn(children));
// Interested in both an output showing <GreenBox />
// and/or the parsed version which shows the stuff inside GreenBox
return (
<p>Logged!</p>
)
}
function App(){
return (
<LogChild>
<ExampleChildComponent />
</LogChild>
)
}
Alternatively, if there's an open source project that I can just study works too. Google is very sparse in examples for this question. Either that or answers are pre React 18.
This question already has answers here:
NEXTJS: getServerSideProps not working into components
(2 answers)
Closed 8 months ago.
I've converted my create-react-app to a next js app to start SSG, I tested the getStaticProps function with a jsonplaceholder API and I was able to retrieve the data and display it, but for some reason it keeps returning undefined when I changed the api.
Here is the code
import React, { useState } from 'react';
export const getServerSideProps = async () => {
const response = await fetch(`https://sample.firebaseio.com/Properties.json?orderBy="/records/0/Address/City"&equalTo="City"`);
const getData = await response.text();
const parsedData = JSON.parse(getData);
const keyName = Object.entries(parsedData);
return {
props: { properties: keyName }
}
}
const PropertiesByCity = ({ properties }) => {
const [getProperties, setProperties] = useState([]);
setProperties(properties);
return (
<div className="map__wrapper">
{getProperties.map(([key, value]) => {
try {
return (
<div key={key} className="listing__wrapper">
<div key={key} className="property__main-photo">
<img key={key} src={value.records[0].Photo.PropertyPhoto[0].PhotoURL} />
<h1 key={key}>{new Intl.NumberFormat('en-US', {
style: 'currency',
currency: 'USD'
}).format(value.records[0].Price).slice(0, -3)}</h1>
<h1 key={key}>{value.records[0].Address.StreetNumber} {value.records[0].Address.StreetName}</h1>
<p key={key}>{value.records[0].Address.City}</p>
</div>
<div key={key} className="listing__info">
<div key={key} className="listing__info-bedbath">
{value.records[0].Building.BedroomsTotal}
<p className="listing__info-bedbath-bottom">Bedrooms</p>
</div>
<div key={key} className="listing__info-bedbath">
{value.records[0].Building.BathroomTotal}
<p className="listing__info-bedbath-bottom">Bathrooms</p>
</div>
</div>
</div>
);
} catch (error) {
return (<p> </p>)
}
}
)}
</div>
);
};
export default PropertiesByCity;
The JSON that is returned from calling this api looks something like this:
{"records":[{"Address":{"AddressLine1":"1234 Sample ","City":"Stoney Point","Country":"Canada","Latitude":"0000000","Longitude":"0000000","PostalCode":"11111","Province":"Ontario","StreetAddress":"1234 Sample","StreetName":"ST. CLAIR","StreetNumber":"1234"},"Phones":{"Phone":[{"_XmlAttributes":{"ContactType":"Business","PhoneType":"Telephone"},"_XmlContent":"(229) 999-2223"},{"_XmlAttributes":{"ContactType":"Business","PhoneType":"Fax"},"_XmlContent":"(111) 111-1111"},{"_XmlAttributes":{"ContactType":"Business","PhoneType":"Toll Free"},"_XmlContent":"(111) 113-1113"}]},"PhotoLastUpdated":"Wed, 01 Sep 2021 15:22:00 GMT","Position":"Sales Person","Websites":{"Website":{"_XmlAttributes":{"ContactType":"Business","WebsiteType":"Website"},"_XmlAttributes":{"ID":"12334566"}},"AlternateURL":{"VideoLink":"https://youtu.be/D-kfhdskjfh"},"Board":"34","Building":{"ArchitecturalStyle":"Ranch","BathroomTotal":"2","BedroomsAboveGround":"3","BedroomsBelowGround":"0","BedroomsTotal":"3","ConstructionStyleAttachment":"Detached","ExteriorFinish":"Stone, Concrete/Stucco","FireplacePresent":"False","FlooringType":"Hardwood, Cushion/Lino/Vinyl","FoundationType":"Concrete","HeatingFuel":"Natural gas","HeatingType":"Forced air","Rooms":{"Room":[{"Dimension":"Measurements not available","Length":"","Level":"Basement","Type":"Utility room","Width":""},{"Dimension":"Measurements not available","Length":"","Level":"Basement","Type":"Storage","Width":""},{"Dimension":"Measurements not available","Length":"","Level":"Main level","Type":"4pc Bathroom","Width":""},{"Dimension":"Measurements not available","Length":"","Level":"Main level","Type":"5pc Ensuite bath","Width":""},{"Dimension":"Measurements not available","Length":"","Level":"Main level","Type":"Laundry room","Width":""},{"Dimension":"Measurements not available","Length":"","Level":"Main level","Type":"Bedroom","Width":""},{"Dimension":"Measurements not available","Length":"","Level":"Main level","Type":"Primary Bedroom","Width":""},{"Dimension":"Measurements not available","Length":"","Level":"Main level","Type":"Living room","Width":""},{"Dimension":"Measurements not available","Length":"","Level":"Main level","Type":"Eating area","Width":""},{"Dimension":"Measurements not available","Length":"","Level":"Main level","Type":"Kitchen","Width":""},{"Dimension":"Measurements not available","Length":"","Level":"Main level","Type":"Foyer","Width":""}]},"StoriesTotal":"1","Type":"House"},"Business":{"Franchise":""},"Features":"Double width or more driveway, Gravel Driveway","Land":{"Acreage":"false","SizeIrregular":"109XIRREG FT","SizeTotalText":"109XIRREG FT"},
The JSON is a series of objects that are similar to this, but not pasting all of them, you get the point.
The code was working just fine with a useEffect in my react app, and this file worked fine when I tested it with a JSONplaceholder API, but for some reason it isn't working when I use my firebase api. I can't seem to find a better way to debug this using console.log() like I can in a client side rendered app.
I am calling this component with a in my index.js file.
I can't seem to debug with console.logs either, it is just returning undefined.
If someone could give me an answer, or a better way to test the app to debug this problem, please let me know.
The answers that I've seen have just stated that the getStaticProps should only be called in the index.js file, but it worked when using the JSonplaceholder API.
I was calling the component in my index.js file, instead of visiting the route in the components file. If I would've went to the URL localhost:3000/components/pull-city, the getStaticProps would have triggered and rendered the data.
I am still getting used to static websites, I am not familiar with the differences in directory configurations, but I'm getting there.
I'm an iOS developer and I've been struggling for what seems like the longest time making my portfolio site from scratch. I've tried a bunch of different technologies and have finally settled on using Gatsby to create it.
So far things have been fairly straightforward but I can not figure out for the life of me how to get a component that looks like the picture below. I've gotten most of the layout design working, but I can't seem to use graphql to query the images I need in the component.
Desired Layout
I've found plenty of Gatsby example templates such as this one and this one that are similar. However the main difference is that each of these only have one image and they seem to be using Gatsby 2.0 instead of 3.0.
I can get one image using "useStaticQuery", however I need access to different images for each component. From my understanding this is not possible to do within a component, only on a page. I also can not pass the image path as a variable to StaticImage either.
export default function App(props) {
const query = useStaticQuery(graphql`
query AppSectionImages {
icon: file(relativePath: { eq: "EzMaxRequest/AppIcon_180.png" }) {
childImageSharp {
gatsbyImageData(
width: 200
placeholder: BLURRED
formats: [AUTO, WEBP, AVIF]
)
}
}
}
`);
const image = getImage(query.icon);
const app = props.app;
return (
<div>
<h1>{app.title}</h1>
<GatsbyImage image={image} />
</div>
);
Result
Can anyone please explain to me how I can get the desired layout in a component?
Edit
Here is some relevant code of what I am doing.
This is my index.js home page.
export default function IndexPage({ data }) {
const projects = data.apps.edges;
return (
<Layout>
<SEO title="Home" />
<HeroSection />
<DescriptionSection />
<div>
{projects.map(({ node: project }) => (
<AppSection app={project} />
))}
</div>
<FooterSection />
</Layout>
);
}
//export page query
export const query = graphql`
query Apps {
apps: allAppsJson(sort: { order: ASC, fields: order }) {
edges {
node {
appLink
title
tagline
moreLink
order
icon
}
}
}
}
`;
Here is the component.
export default function App(props) {
const query = useStaticQuery(graphql`
query AppSectionImages {
icon: file(relativePath: { eq: "EzMaxRequest/AppIcon_180.png" }) {
childImageSharp {
gatsbyImageData(
width: 200
placeholder: BLURRED
formats: [AUTO, WEBP, AVIF]
)
}
}
}
`);
const image = getImage(query.icon);
const app = props.app;
return (
<div>
<h1>{app.title}</h1>
<GatsbyImage image={image} alt={app.title} />
</div>
);
}
You have a few options:
Query for all of your image data in your page query and prop-drill the data to the component that uses it to display the image.
Using Gatsby v3+, hardcode the image references for each component using the new StaticImage component.
If you have a single component used multiple times with different content/images, but a static parent component with your content, you can leverage option #2 above but pass the image component down as a prop or children.
I can confirm that this has neither been asked nor been addressed anywhere. I am currently working on a website using Gatsby, Strapi and Antd for the design. I am using the the rich text editor for one of the content types where I have put all my markdown content. However, when I try to display the actual content on the webpage, the styling is completly nuked. I figured this was because the content uses normal HTML elements like <h1> and <p> instead of the antd components like <Title> or <Text>.
So I did some researches and found that Antd has a markdown.less in their source, which I figure is used to style the markdown in their documentation. I haven't found the same after scouring the source code inside the node modules folder. Does this mean that Antd does not support styling for markdown or am I missing something here?
Btw I am using the react-markdown library to display the all the markdown. I have also posted all the relevant code below.
template.tsx
const ProductTemplate: React.FC<Props> = ({ data }: Props) => {
const {
...
} = data
const {
product: { strapiId: selectedKeyProp },
} = data
return (
<Layout>
<AntLayout>
<ProductSidebar
selectedKeyProp={selectedKeyProp}
productsInfo={productsInfo}
>
<ProductInfo product={product} />
</ProductSidebar>
</AntLayout>
</Layout>
)
}
export const query = graphql`
...
`
export default ProductTemplate
page-component.tsx
const ComponentName = ({ data }) => {
const {
...
} = data
console.log(data)
return (
<Layout>
<AntLayout>
<ProductSidebar productsInfo={productsInfo}>
<div style={{ display: "unset", padding: "15px 35px" }}>
<ReactMarkdown className="markdown" children={content} />
</div>
</ProductSidebar>
</AntLayout>
</Layout>
)
}
export const query = graphql`
...
`
export default ComponentName
There problem can be pointed out here I guess. The <ReactMarkdown> receives the markdown content in the children props. But once the content is displayed to the page, the styling, as I mentioned above, is nuked out.
I raised this issue on github here. According to the devs, there is currently no markdown support for antd. As I thought, the markdown.less file linked above was just for the markdown in their documentation.
One way to get around this solution is to include the tags exactly as specified by antd component elements. For example, instead of a # or <h1> for a header, we can use <h1 class="antd typography">, though this definitely is painful and prone to error.
This other solution would be to use MDX, which support jsx inside markdown.
About a month late, but I just ran into this: I needed a way to change the styling of html generated by the react-markdown React component. I am using react-markdown in a NextJS app, and using Antd as my component library. I am additionally using react-syntax-highlighter and react-syntax-highlighter-prismjs for handling lighting codeblocks
While there is no support in Antd for markdown, there is support for custom components in react-markdown! react-markdown allows you to override the rendering engine and replace the individual components with your own, so I went through and replaced a bunch of them with Antd components:
Fair warning: This was my first pass to make sure this worked, it doesn't for instance handle checkboxes inside list item inputs.
import { Prism as SyntaxHighlighter } from "react-syntax-highlighter";
import {coy} from "react-syntax-highlighter/dist/cjs/styles/prism/prism";
import gfm from 'remark-gfm';
import { Typography } from 'antd';
const { Title, Text } = Typography;
import { List } from 'antd';
const mymarkdowndata = '
# A heading
Some text
## A second heading
* List!
* Has
* Many
* Items!
'
const renderers = {
heading: function Heading(props) {
return <Title level={props.level}>{props.children}</Title>;
},
list: function MakeList(props) {
return <List bordered>{props.children}</List>
},
listItem: function MakeListItem(props) {
return <List.Item>{props.children}</List.Item>
},
inlineCode: function makeInlineCode(props) {
return <Text code>{props.children}</Text>
},
code: function makeCodeBlock(props) {
return <SyntaxHighlighter language={props.language} style={coy}>{props.value}</SyntaxHighlighter>
},
blockquote: function makeBlockQuote(props) {
return <Text type="secondary">{props.children}</Text>
}
};
Then inside your component rendering function:
render() {
return <ReactMarkdown renderers={renderers} plugins={[gfm]} children={mymarkdowndata} />
}
This question already has an answer here:
react dynamic import using a variable doesn't work
(1 answer)
Closed 2 years ago.
I am passing a prop (which contains a string link) into a component. This props is then used inside the src property of <img /> but this causes a broken image instead. What is the correct way of doing this without using the import...from... method in the beginning of my component. The code below shows other alternatives that i tried which dont work.
class Entry extends React.Component {
render() {
const link = '../../images/company-logo.png';
const image = require(link); //error: cannot find module
const imagee = require('../../images/company-logo.png'); //works fine, but not ideal
return (
<div className="entry">
<img src={this.props.imageLink}/> //results in a broken image
<img src={link}/> //results in a broken image
<img src={imagee}/> //works fine
</div>
);
}
}
You need to import the image first and then either use it directly as value of src attribute on img element or pass it to some other component as a prop
import myImg from '../../images/company-logo.png';
now either use myImg directly as a value of src attribute
<img src={myImg}/>
or pass it down as a prop
<div className="entry">
<SomeComponent img={myImg} />
</div>
Actually you can pass image src via props but some where you have to import it. You have to do that because React projects mostly use Webpack to build the code and the images also be built with JS code.
I know there are 2 options which might help you solve this problem.
Import image from Parent Component and pass it to the child one:
import Image from '../images/image-01.png';
function ParentComponent {
return <ChildComponent img={Image} />;
}
function ChildComponent(props) {
return <img src={props.img} />;
}
Import all images inside a folder and use it as an image dictionary by using require.context:
function importAll(r) {
return r.keys().map(r);
}
const images = importAll(require.context('../images', false, /\.(png|jpe?g|svg)$/));
function ChildComponent(props) {
const link = 'image-01.png'; // or link = props.parentLink;
return <img src={images[link]} />;
}
You could find more information about option 2 in this answer.
Hope it could help you solve your problem.