How does getStaticProps() render fetched data in the initial HTML file? - reactjs

According to the Next.js official site, pre-rendered pages first display the pre-rendered HTML and then hydrate it by initializing the React components and making it interactive (adding the event listeners). If this is the case, how is it that the props passed to the component via getStaticProps() (see generic example below) manipulate the initial HTML render? Isn't the React code just running server-side to inject the desired data into the pre-rendered HTML, then running again later to hydrate it?
export async function getStaticProps() {
// Get external data from the file system, API, DB, etc.
const data = ...
// The value of the `props` key will be
// passed to the `Home` component
return {
props: ...
}
}

getStaticProps() works even when there is no "server-side", i.e. when you've next exported your site to just static files. Hence it's not "React code just running server-side".
If you look at a next exported page, you'll see it's something like
<!DOCTYPE html>
<html lang="en">
<head>
...
<script src="/_next/client-side-script-stuff-here.js"></script>
</head>
<body>
<div id="__next" data-reactroot="">
<h1>Bare rendered HTML here, no scripts...</h1>
</div>
<script id="__NEXT_DATA__" type="application/json">{"props":{"pageProps":{"somePageProp":...}}, "__N_SSG":true},"page":"/","query":{},"buildId":"..."}</script>
</body>
</html>
and it's the client-side-script-stuff-here.js in conjunction with the injected __NEXT_DATA__ that has React and Next's machinery hydrate the page.

Related

Static HTML not showing in "View Page Source" even though data was fetched by getStaticProps()

This is a 2-part question that is related (somewhat):
Part I: Rendering HTML output via getStaticProps in the "View Page Source" view.
For SEO reasons, I'd like my Next.js frontend application to return HTML output in the "View Page Source" view (in Google Chrome) instead of JSON.
I'm using getStaticProps() to return some data from my backend Django app. I built the same page and functionality in React Remix Run, and I CAN see my HTML output. However, I'd like this to render the full HTML (instead of JSON) in my Next.js app so that I don't have to rewrite the entire application in Remix.
Here's my code:
Next.js code
export const getStaticProps = async (context) => {
const { slug } = context.params
const product = await fetchProductDetails(slug)
const otherProductsList = await fetchProducts()
const error = product.error ? product.error : ''
return {
props: {
product,
otherProductsList,
error
},
revalidate: 15
}
}
// Functional component
const ProductDetails = ({ product, otherProductsList, error }) => {
// my client side code...
return (
// my JSX code that displays the product data using {product.name},
{product.price}, etc.
// The "View Page Source" view does not show this JSX element as an h1 tag.
// Instead, it shows all of the JSON being returned from the DB.
<h1>{product.name}</h1>
)
}
Example source code output shown in "View Page Source" view:
<!DOCTYPE html>
<html>
<head>
<meta charSet="utf-8"/>
<meta name="viewport" content="width=device-width"/>
<meta name="next-head-count" content="2"/>
<link rel="preload" href="/_next/static/css/75fa4eff9b884fe2.css" as="style"/>
<link rel="stylesheet" href="/_next/static/css/75fa4eff9b884fe2.css" data-n-g=""/>
<link rel="preload" href="/_next/static/css/eafeef7863427731.css" as="style"/>
<link rel="stylesheet" href="/_next/static/css/eafeef7863427731.css" data-n-p=""/>
<noscript data-n-css=""></noscript>
<script defer="" nomodule="" src="/_next/static/chunks/polyfills-5cd94c89d3acac5f.js"></script><script src="/_next/static/chunks/webpack-dd2ed5d38a88a4b3.js" defer=""></script><script src="/_next/static/chunks/framework-0f8b31729833af61.js" defer=""></script><script src="/_next/static/chunks/main-83d2e1974cccf08f.js" defer=""></script><script src="/_next/static/chunks/pages/_app-fa3d7135221d445a.js" defer=""></script><script src="/_next/static/chunks/885-9d94ce365372990b.js" defer=""></script><script src="/_next/static/chunks/341-e930103f26cccc8f.js" defer=""></script><script src="/_next/static/chunks/418-874816ac548e7a1e.js" defer="">
// LOTS OF JSON OUTPUT HERE - THIS SHOULD BE HTML INSTEAD
</script>
</html>
Any idea why Next.js is returning JSON in the "View Page Source" view instead of JSON despite using getStaticProps() to fetch the data?
NOTE: I also tried removing my useEffect() hook (which is currently used to handle "success" operations), and I'm still getting JSON output. Also to note, the HTML markup actually shows as expected in the Google Chrome Inspect window.
Part 2: Setting <meta property> tags in Next.js on the page level.
I am also trying to set <meta property> tags on the page level (every page has a different meta property) so that my thumbnails and title text can be accessed through link sharing on social media platforms. For example, if I copy a link to a product page from my Next.js application, and post it to my Twitter feed, Twitter should pull the product thumbnail, title, description, etc.
I already know what code to write to make this work, but as related to the above problem, these tags are not showing in "View Page Soruce".
Are these two problems related?

Is it possible to create Html content from json data in react native?

I am trying to create an html content from json data in react native. Basically i want to generate PDF from html content which i have already done using static html content in my react native app, but now we want to generate pdf with dynamic content and decide the data to be in json and convert them into html content and then generate pdf from the html content.
Our web team has already done this rendering the json data into dom and then generating pdf from the dom using react.
Is it possible to achieve the same using react native.
You can achieve this like doing this- (use backticks instead of single or dubble quotes)
const JsonData = {
title: 'My PDF File',
image: 'https://raw.githubusercontent.com/AboutReact/sampleresource/master/pdf.png'
}
const html = `
<html>
<head>
<meta charset="utf-8">
<title>MyPDF</title>
<style>${htmlStyles}</style>
</head>
<body>
<h1>${JsonData.title}</h1>
<img src=`${JsonData.image}` />
{JsonData.otherData}
</body>
</html>
`;
Checkout example for this:
https://github.com/vishalpwr/react-native/tree/master/CreatePdf

How can I inject arbitrary string HTML content into the head of my gatsbyjs site?

I have a GatsbyJS site that I am working on where the main content source is a Wordpress install. One of the things I like to add to my sites is the ability to have placeholder areas in the site where I can control the content via the CMS. Usually I have a header_scripts area that goes at the bottom of the <head> tag, a body_scripts area that goes at the start of the <body> tag, and a footer_scripts area that goes at the bottom of the page <body>. With these three, I can usually integrate third-party add-ins pretty easily without having to do code deployments.
Sometimes I need to embed stylesheets, sometimes I need to embed script tags, and sometimes I need to throw in <meta> tags. Really the content could be anything. This data comes back as a raw string from my Wordpress GraphQL endpoint.
So now my question is, how do I get this content injected into my Gatsby site in the following places:
<html>
<head>
...
{header_scripts}
</head>
<body>
{body_scripts}
...
{footer_scripts}
</body>
</html>
I've found so far that I can just include the body_scripts and footer_scripts in a fairly regular manner in my Gatsby page template. In gatsby-node.js, I pass in the property values using the pageContext. It's kind of a bummer that they need to be wrapped in a <div /> tag, but they seem to work just fine.
import React from 'react'
export default class PageTemplate extends React.Component {
render = () => {
return (
<React.Fragment>
{this.props.pageContext.bodyScripts && (
<div dangerouslySetInnerHTML={{__html:this.props.pageContext.bodyScripts}} />
)}
{/* my page content here */}
{this.props.pageContext.footerScripts && (
<div dangerouslySetInnerHTML={{__html:this.props.pageContext.footerScripts}} />
)}
</React.Fragment>
)
}
}
Now for the real question. I am stumped on how to get the dynamic content from the header_scripts into the Gatsby server-side-rendering <head> tag. The closest thing I have found to being able to inject content into the head is to leverage the gatsby-ssr.js onRenderBody function. However, this seems to require pre-determined React component instances in order to function. I can't just pass it in plain raw string content and see the output in the page source:
export const onRenderBody = async ({
pathname,
setHeadComponents,
setHtmlAttributes,
setBodyAttributes,
setPreBodyComponents,
setPostBodyComponents,
setBodyProps
}, pluginOptions) => {
setHeadComponents(['<script>alert("hello");</script>'])
}
This results in an escaped string getting inserted into the <head> tag:
<html>
<head>
...
<script>alert("hello");</script>
</head>
<body>
...
</body>
</html>
I'm at a loss as to how to proceed. I can't just wrap my string in a <div /> tag like in the body because div tags can't go inside the head tag. I can't think of any head-capable HTML tags that would accept this kind of content.
The only idea I've had is to actually parse the string content into full React components. This seems daunting given the number of possible tags & formatting that I would need to support.
Am I going about this the wrong way? How can I get my arbitrary content into my Gatsby site's head tag?
It's a broad question and it will need some trials and errors to ensure that it's fully working without caveats in all scenarios but, among the things you've tried, you can add a few more options to the list to check which ones fit better.
Regarding the body_scripts and footer_scripts both can be inserted using the:
<div dangerouslySetInnerHTML={{__html:this.props.pageContext.footerScripts}} />
In any desired page or template. For the header_scripts and the meta tags (SEO), you can use the <Helmet> component. Basically, using this component, everything that is wrapped inside, it's becomes transpiled inside the <head> tag once compiled.
export default class PageTemplate extends React.Component {
render = () => {
return (
<React.Fragment>
<Helmet>
{this.props.pageContext.headerScripts && (
<div dangerouslySetInnerHTML={{__html:this.props.pageContext.headScripts}} />
)}
</Helmet>
{this.props.pageContext.bodyScripts && (
<div dangerouslySetInnerHTML={{__html:this.props.pageContext.bodyScripts}} />
)}
{/* my page content here */}
{this.props.pageContext.footerScripts && (
<div dangerouslySetInnerHTML={{__html:this.props.pageContext.footerScripts}} />
)}
</React.Fragment>
)
}
}
However, if the data comes from a CMS, it won't be available in the SSR yet, so, one easy thing you can do is to customize the outputted HTML (html.js) that Gatsby generates in each compilation. From the docs:
Customizing html.js is a workaround solution for when the use of the
appropriate APIs is not available in gatsby-ssr.js. Consider using
onRenderBody or onPreRenderHTML instead of the method above. As a
further consideration, customizing html.js is not supported within a
Gatsby Theme. Use the API methods mentioned instead.
Run:
cp .cache/default-html.js src/html.js
Or manually, copy the .cache/default-html.js file and paste it /src folder. There you can customize the final HTML.

Can't embed Facebook post into Next JS generated page

I am trying to embed a public FB post into the main page of my application. I am following FB guide and it's pretty simple. It works when I do it in .html file, but doesn't with Next JS.
Basically, instructions are that you need to insert this right after the body opening tag
<div id="fb-root"></div>
<script async defer crossorigin="anonymous"
src="https://connect.facebook.net/en_US/sdk.js#xfbml=1&autoLogAppEvents=1&version=v9.0&appId={appId}" nonce={someNonce}"></script>
and then you put the other part wherever you want.
I even created a custom _document.js file and included this script, I can also see it in the browser. But the post does not get loaded.
Anyone had this kind of issue?
Assuming you already have the JS SDK loaded in your document, like you mentioned (you might also load the script on-demand via JavaScript if preferred).
// pages/_document
class MyDocument extends Document {
render() {
return (
<Html lang="en">
<!-- additional code -->
<body>
<!-- additional code -->
<script
async
defer
src="https://connect.facebook.net/en_US/sdk.js#xfbml=1&version=v3.2"
/>
</body>
</Html>
);
}
}
You can then render a Facebook post inside one of your components with:
<div
className="fb-post"
data-href="https://www.facebook.com/20531316728/posts/10154009990506729/"
/>
For further details refer to the official Embedded Posts documentation.

React View source in Browser Newbe React

I have a simple React site up and when I go to "View Source" in Chrome, It doesnt show much of the markup.Mostly JS imports. But if I click on an element with Dev tools up, it shows me the html as I would normally see it.
Can someone explain this to me?
Thanks
View source will have the the content what you have in build/index.html or public/index.html
index.html will have some <script> tags. Browser executes these JavaScript files in script tag and renders the page. We can say this as dynamic code or runtime generated html, css and other code.
So View Source will show only static contents, that is what you have in index.html. Its same as if you open index.html in any editor like notepad.
Where as when using dev tools you will see all runtime generated code. That is what dev tools indend to do.
And if you need to see the React components, state, props and other details, you need to use React dev tools for chrome
A simple example would be:
index.html
<html>
<body>
<div id="app"></div>
<script>
document.getElementById("app").innerHTML = "Hello World";
</script>
</body>
</html>
You will see the above code in View Source.
You will see below code in dev tools
<html>
<body>
<div id="app">Hello World</div>
<script>
document.getElementById("app").innerHTML = "Hello World";
</script>
</body>
</html>
Hope this is clear.
The actual source is mostly JS, between libraries that react uses, libraries you've imported, and the JS you've written.
When you write a view, typically in .jsx format, it is JS that is translated into HTML. So after the dom has populated by using the combination of the libraries and what you have written it is then available to view the html in the DOM, but the source will still only display the JS.
Sudo example
example.jsx
...
render() {
const example = ['a', 'b', 'c'];
return ( <div> { example.map((val) => (<p>{val}</p>)) } </div> }
}
...
Source
...
require('example.js')
...
DOM
...
<div>
<p>a</p>
<p>b</p>
<p>c</p>
</div>
...

Resources