I have updated this to use react-helmet-async in place of react-helmet.
I have a React application that has implemented server side rendering. I am following the steps on this site: Documentation for react-helmet-async, to implement the react-helmet-async functionality to dynamically assign meta and title. I am using a provider and have added the Helmet component to the App.js and the learnerSetupContainer.js scripts. When I look at the source code for both these pages I can that the title and meta for the home page is properly updated but the learnerSetupContainer is not being updated and is showing the same title and meta as the home page,
<title data-rh="true" itemprop="name" lang="en">Welcome to our Homepage</title>
<meta data-rh="true" name="description" content="Teach Learn Game"/><meta data-rh="true" name="viewport" content="minimum-scale=1, initial-scale=1, width=device-width, shrink-to-fit=no"/>
I see other stackoverflow questions that say, "It's not documented in the readme, but you have to add data-react-helmet="true" to your meta tags that you want to replace with react-helmet." I tried adding this string but there was no change. Maybe I am not adding correctly. Can anyone see what I am doing wrong?
Below is the code I am using:
template.js
export default ({ markup, helmet , css }) => {
return `<!doctype html>
<html ${helmet.htmlAttributes.toString()}>
<head>
<!-- Global site tag (gtag.js) - Google Analytics -->
<script async src="https://www.googletagmanager.com/gtag/js?id=UA-1234567-9">
</script>
<script>
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', 'UA-123456789-9');
</script>
<script data-ad-client="ca-pub-123456789012345" async src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"></script>
<meta charset="utf-8">
<meta name="msvalidate.01" content="1234567890ASDFGHJJ" />
<meta name="google-site-verification" content="1234567890ASDFGHJKL" />
${helmet.title.toString()}
${helmet.meta.toString()}
${helmet.link.toString()}
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto:100,300,400">
<link rel="stylesheet" href="https://fonts.googleapis.com/icon?family=Material+Icons">
<style>
a{
text-decoration: none;
color: #bb4d00
}
</style>
</head>
<body ${helmet.bodyAttributes.toString()}>
<div id="root">${markup}</div>
<style id="jss-server-side">${css}</style>
<script type="text/javascript" src="/dist/bundle.js"></script>
</body>
</html>`;
};
express.js (only code relevant to react-helmet)
import {Helmet, HelmetProvider} from "react-helmet-async";
app.get("*", (req, res) => {
const sheets = new ServerStyleSheets();
const context = {};
const helmetContext = {};
const markup = ReactDOMServer.renderToString(
//const markup = ReactDOMServer.renderToStaticMarkup(
sheets.collect(
<StaticRouter location={req.url} context={context}>
<ThemeProvider theme={theme}>
<HelmetProvider context = {helmetContext}>
<MainRouter />
</HelmetProvider>
</ThemeProvider>
</StaticRouter>
)
);
const {helmet} = helmetContext;
console.log ("Express - markup = ", markup);
console.log ("Express - helmet = ", helmet);
console.log ("Express - helmetContext = ", helmetContext);
console.log ("Express - helmet.title.toString() = ", helmet.title.toString());
console.log ("Express - helmet.meta.toString() = ", helmet.meta.toString());
if (context.url) {
return res.redirect(303, context.url);
}
const css = sheets.toString();
res.status(200).send(
Template({
markup: markup,
helmet: helmet,
css: css,
})
);
});
App.js (only code relevant to react-helmet)
import {Helmet, HelmetProvider} from "react-helmet-async";
return (
<>
<React.StrictMode>
<BrowserRouter>
<ThemeProvider theme={responsivetheme}>
<SpeechDataProvider>
<CriteriaProvider>
<RandomOptionsProvider>
<HelmetProvider>
<MainRouter />
</HelmetProvider>
</RandomOptionsProvider>
</CriteriaProvider>
</SpeechDataProvider>
</ThemeProvider>
</BrowserRouter>
</React.StrictMode>
</>
);
MenuRouter.js (only code relevant to react-helmet)
import {Helmet, HelmetProvider} from "react-helmet-async";
return (
<div>
<Helmet
data-rh="true"
htmlAttributes={{lang: "en", amp: undefined}} // amp takes no value
//titleTemplate="%s | React App"
titleAttributes={{itemprop: "name", lang: "en"}}
title="Welcome to our Homepage"
meta={[
{name: "description", content: "Teach Learn Game"},
{name: "viewport", content: "minimum-scale=1, initial-scale=1, width=device-width, shrink-to-fit=no"},
]}
/>
</div>
learnerSetupContainer.js (only code relevant to react-helmet)
import {Helmet, HelmetProvider} from "react-helmet-async";
return (
<>
<div>
<Helmet>
<title data-rh="true">Welcome to our Learner Setup Page </title>
<meta data-rh="true" name="description" content="Game Options: Vocabulary, Math, History and Geography" />
<meta data-rh="true" name="viewport" content="minimum-scale=1, initial-scale=1, width=device-width, shrink-to-fit=no" />
</Helmet>
</div>
</>
Related
import { getMeta } from '#/helpers/globalHelpers';
import Head from 'next/head';
import { useDispatch, useSelector } from 'react-redux';
import { increment, decrement, selectValue, bulkUpdate, reset } from '#/slicers/propertySlice';
import Link from 'next/link';
export default function Home({ meta }) {
const data = useSelector(selectValue);
const dispatch = useDispatch();
console.log(meta);
return (
<>
<Head>
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta name="og:title" content={meta.title} />
<meta name="og:description" content={meta.metadescription} />
<meta name="description" content={meta.metadescription} />
<meta name="keywords" content={meta.keywords} />
<title>{meta.title}</title>
</Head>
Hello world!
</>
)
}
export async function getServerSideProps({ resolvedUrl }) {
const pageData = await getMeta(resolvedUrl);
return {
props: { meta: pageData[0] }
}
}
I am working with next.js yarn and redux. I am trying to get my SEO data from my api and render is wit ssr. But it is rendering at the client side and SEO crawler tools (i am using screaming frog) can't see my title and description
Here is my code. I couldn't understand why my Head tag is rendering at the client side. Anyone can help?
Extra info, i checked _document.js, tag is correctly used and defined.
import { Html, Head, Main, NextScript } from 'next/document'
import Script from 'next/script'
export default function Document() {
return (
<Html>
<Head />
<body>
<Main />
<NextScript />
<Script src="assets/js/jquery.min.js"></Script>
<Script src="assets/bootstrap/js/bootstrap.min.js"></Script>
<Script src="assets/js/aos.min.js"></Script>
<Script src="assets/js/bs-init.js"></Script>
<Script src="assets/js/checkbox-show-hide.js"></Script>
<Script src="assets/js/dropdown-saver.js"></Script>
<Script src="assets/js/floatsearch.js"></Script>
<Script src="assets/js/heroclean.js"></Script>
<Script src="assets/js/location-list.js"></Script>
<Script src="assets/js/mobilemenu.js"></Script>
</body>
</Html>
)
}
I am having the Nextjs-based Web App for my Portfolio Website, where I've defined some common meta tags in the _document.jsx file and I have a few static pages in which in the browser tab I can see the title, but when I open the Page Source those titles are missing.
My _document.jsx file code are below:
// Next
import Document, { Html, Head, Main, NextScript } from 'next/document';
import Script from 'next/script';
// styled-components
import { ServerStyleSheet } from 'styled-components';
const APP_NAME = `Dhaval Vira Resume | CV | Portfolio`;
const APP_DESC = `A skilled full-stack developer who has worked on projects ranging from small personal sites to large enterprise systems.`;
const APP_URL = `https://dhavalvira.com`;
class MyDocument extends Document {
static async getInitialProps(ctx) {
const sheet = new ServerStyleSheet();
const originalRenderPage = ctx.renderPage;
try {
ctx.renderPage = () =>
originalRenderPage({
enhanceApp: (Component) => (props) =>
sheet.collectStyles(<Component {...props} />),
});
const initialProps = await Document.getInitialProps(ctx);
return {
...initialProps,
styles: [initialProps.styles, sheet.getStyleElement()],
};
} finally {
sheet.seal();
}
}
render() {
return (
<Html lang='en-US'>
<Head>
{/* General Meta Tags */}
<meta charSet='utf-8' />
<meta httpEquiv='X-UA-Compatible' content='IE=edge' />
<link rel='icon' href='/favicon.ico' />
{/* Meta Tags */}
<meta name='application-name' content={APP_NAME} />
<meta name='description' content={APP_DESC} />
<meta name='author' content='Dhaval Vira' />
<meta name='robots' content='index, follow' />
<meta name='rating' content='general' />
<link rel='canonical' href={APP_URL} />
<meta httpEquiv='Content-Type' content='text/html; charset=utf-8' />
<meta name='language' content='English' />
<meta name='revisit-after' content='1 day' />
<meta name='creationdate' content='14-Feb-2022' />
<meta name='distribution' content='global' />
<meta
name='keywords'
content='CV, resume, online cv, online resume, professional resume, portfolio, next js developer, nextjs developer, freelance developer, full stack developer, full-stack developer, freelance full stack developer, freelance full-stack developer, freelance next js developer, freelance next.js developer, freelance nextjs developer'
/>
{/* Open Graph */}
<meta property='og:url' content={APP_URL} />
<meta property='og:type' content='website' />
<meta property='og:title' content={APP_NAME} />
<meta property='og:description' content={APP_DESC} />
<meta property='og:image' content='/D_V_Cropped.png' />
{/* Twitter Meta Tags */}
<meta name='twitter:card' content='summary_large_image' />
<meta property='twitter:domain' content={APP_URL} />
<meta property='twitter:url' content={APP_URL} />
<meta name='twitter:title' content={APP_NAME} />
<meta name='twitter:description' content={APP_DESC} />
<meta name='twitter:image' content='/D_V_Cropped.png' />
{/* Some Extra Tags */}
<meta name='apple-mobile-web-app-capable' content='yes' />
<meta
name='apple-mobile-web-app-status-bar-style'
content='default'
/>
<meta name='apple-mobile-web-app-title' content={APP_NAME} />
<meta name='description' content={APP_DESC} />
<meta name='format-detection' content='telephone=no' />
<meta name='mobile-web-app-capable' content='yes' />
<meta name='theme-color' content='#000000' />
<link rel='manifest' href='/manifest.json' />
<link rel='apple-touch-icon' href='/D_V_Cropped.png' />
{/* Icons */}
<link
rel='apple-touch-icon'
sizes='57x57'
href='/icons/apple-icon-57x57.png'
/>
<link
rel='apple-touch-icon'
sizes='60x60'
href='/icons/apple-icon-60x60.png'
/>
<link
rel='apple-touch-icon'
sizes='72x72'
href='/icons/apple-icon-72x72.png'
/>
<link
rel='apple-touch-icon'
sizes='76x76'
href='/icons/apple-icon-76x76.png'
/>
<link
rel='apple-touch-icon'
sizes='114x114'
href='/icons/apple-icon-114x114.png'
/>
<link
rel='apple-touch-icon'
sizes='120x120'
href='/icons/apple-icon-120x120.png'
/>
<link
rel='apple-touch-icon'
sizes='144x144'
href='/icons/apple-icon-144x144.png'
/>
<link
rel='apple-touch-icon'
sizes='152x152'
href='/icons/apple-icon-152x152.png'
/>
<link
rel='apple-touch-icon'
sizes='180x180'
href='/icons/apple-icon-180x180.png'
/>
<link
rel='apple-touch-icon'
sizes='192x192'
href='/icons/apple-icon-precomposed.png'
/>
<link
rel='icon'
type='image/png'
sizes='192x192'
href='/icons/android-icon-192x192.png'
/>
<link
rel='icon'
type='image/png'
sizes='32x32'
href='/icons/favicon-32x32.png'
/>
<link
rel='icon'
type='image/png'
sizes='96x96'
href='/icons/favicon-96x96.png'
/>
<link
rel='icon'
type='image/png'
sizes='16x16'
href='/icons/favicon-16x16.png'
/>
<meta name='msapplication-TileColor' content='#54ca95' />
<meta name='msapplication-TileImage' content='/ms-icon-144x144.png' />
<meta name='msapplication-TileImage' content='/ms-icon-150x150.png' />
<meta name='msapplication-TileImage' content='/ms-icon-310x310.png' />
<meta name='theme-color' content='#54ca95' />
{/* Google Tag Manager */}
// GTM iFrame Tag
{/* LinkedIn Insight Tags */}
// LinkedIn Insight Scrip Tags
{/* Google Tag Manager */}
// Google Tag Manager Script Tag
</Script>
{/* Schema.org */}
// Schema.org Script Tag
</Head>
<body>
<Main />
<NextScript />
</body>
</Html>
);
}
}
export default MyDocument;
Below code is from blogs.jsx in which the title inside the <Head></Head> tag remains static <Head><title>Blog :: Dhaval Vira Resume</title></Head>. this title is visible in the Chrome Tab, but when I open the Page Source, that time it's missing.
Below is the code from pages/blog/[index].js Page, and when I open the below Page Source - all those Meta Tags are missing.
const BlogPost = (props) => {
const router = useRouter();
useEffect(() => {
Prism.highlightAll();
}, []);
// destructuring props
const { singleData, ...other } = props;
return (
<Fragment>
<Head>
<title>{singleData[0].blogTitle} :: Dhaval Vira Resume</title>
<meta name='description' content={singleData[0].seoDescription} />
<meta name='keywords' content={singleData[0].tags.map((tag) => tag)} />
{/* OG Meta Tags */}
<meta property='og:title' content={singleData[0].seoTitle} />
<meta
property='og:description'
content={singleData[0].seoDescription}
/>
<meta property='og:type' content='article' />
<meta
property='og:url'
content={`${process.env.SHARE_URL}${router.asPath}`}
/>
<meta
name='image'
property='og:image'
content={singleData[0].displayImageUrl}
/>
{/* Twitter Meta Tags */}
<meta property='twitter:title' content={singleData[0].seoTitle} />
<meta
property='twitter:description'
content={singleData[0].seoDescription}
/>
<meta
property='twitter:image'
content={singleData[0].displayImageUrl}
/>
<link
rel='canonical'
href={`${process.env.SHARE_URL}${router.asPath}`}
/>
</Head>
<div>{/* HTML Code goes here */}</div>
</Fragment>
);
};
export default BlogPost;
export const getServerSideProps = async (context) => {
const { index } = context.params;
const response = await axios({
url: `${process.env.SERVER_URL}/api/get-single-blog-detail?slug=${index}`,
method: 'GET',
validateStatus: function (status) {
return status >= 200 && status < 599;
},
});
switch (response.status) {
case 200:
return {
props: {
singleData: response.data.comments,
},
};
case 500:
return {
redirect: {
destination: '/500',
permanent: false,
},
};
default:
return {
redirect: {
destination: '/404',
permanent: false,
},
};
}
};
Also, if I'm sharing the link of my Portfolio Site with anyone (WhatsApp or LinkedIn) at that place also when the title is coming up, it's coming from either Static Page or Dynamic Page, it's coming from _document.jsx file.
Below is the _app.jsx code:
// React
import React, { Fragment, useEffect, useState } from 'react';
// next component
import Head from 'next/head';
import Router from 'next/router';
// Preloader Component
import Loader from '../Loader/loader';
// react-hot-toast
import { Toaster } from 'react-hot-toast';
// NProgress Package & CSS
import NProgress from 'nprogress';
import '../styles/nprogress.css';
Router.events.on('routeChangeStart', NProgress.start);
Router.events.on('routeChangeError', NProgress.done);
Router.events.on('routeChangeComplete', NProgress.done);
import '../styles/globals.css';
// Google Firebase - Analytics - SDK
import { getAnalytics, logEvent } from 'firebase/analytics';
// Utils Func for Firebase
import { app } from '../utils/firebase';
function MyApp(props) {
const { Component, pageProps } = props;
const [loading, setLoading] = useState(true);
useEffect(() => {
setTimeout(() => {
setLoading(false);
}, 4000);
}, []);
if (loading) {
return (
<Fragment>
<Head>
<title>Dhaval Vira Resume</title>
<meta
name='description'
content='online resume website of Dhaval Vira, cv of Dhaval Vira, portfolio of Dhaval Vira'
/>
<link rel='icon' href='/favicon.ico' />
</Head>
<Loader />
</Fragment>
);
} else {
return (
<Fragment>
<Head>
<title>Dhaval Vira Resume</title>
<meta
name='description'
content='online resume website of Dhaval Vira, cv of Dhaval Vira, portfolio of Dhaval Vira'
/>
<link rel='icon' href='/favicon.ico' />
</Head>
<Component {...pageProps} />
{/* react-hot-toast */}
<Toaster position='top-right' reverseOrder={false} />
</Fragment>
);
}
}
export default MyApp;
In _app.jsx, you're forcing client-side rendering of your pages content after 4s have passed due the the loading logic you have there. This means that when the pages gets pre-rendered on the server you're only getting the Loader component in the HTML sent from the server.
Remove the loading logic entirely so that your pages can be server-side rendered properly.
function MyApp({ Component, pageProps }) {
return (
<Fragment>
<Head>
<title>Dhaval Vira Resume</title>
<meta
name='description'
content='online resume website of Dhaval Vira, cv of Dhaval Vira, portfolio of Dhaval Vira'
/>
<link rel='icon' href='/favicon.ico' />
</Head>
<Component {...pageProps} />
{/* react-hot-toast */}
<Toaster position='top-right' reverseOrder={false} />
</Fragment>
);
}
I created a blog website with reactjs, I want to implement a dynamic meta tag so that when user share each link on any social media platform, the meta tag description for each blog will show up.
I saw this tutorial and I implement this in my project, but The meta tags are not showing
https://blog.logrocket.com/adding-dynamic-meta-tags-react-app-without-ssr/
here is my route
import React from "react"
import './App.css';
import Home from "./pages/home/home";
// import { useDispatch, useSelector } from "react-redux";
import {Routes, Route} from "react-router-dom"
import Auth from "./pages/Auth/Auth";
import Singlepost from "./pages/home/singlepost";
function App() {
return (
<div>
{/* <Navbar /> */}
<Routes>
<Route exact path ='/' caseSensitive={false} element={<Home/>} />
<Route path='/auth' caseSensitive={false} element={<Auth />}/>
<Route path='/:topic' caseSensitive={false} element={<Singlepost />} />
</Routes>
</div>
);
}
export default App;
Here is my server below
const express = require('express');
const axios = require('axios')
const path = require('path');
const fs = require("fs");
const app = express();
const PORT = process.env.PORT || 8000;
const indexPath = path.resolve(__dirname, '..', 'build', 'index.html');
// static resources should just be served as they are
app.use(express.static(
path.resolve(__dirname, '..', 'build'),
{ maxAge: '30d' },
));
// here we serve the index.html page
app.get('/*', async(req, res, next) => {
fs.readFile(indexPath, 'utf8', (err, htmlData) => {
if (err) {
console.error('Error during file reading', err);
return res.status(404).end()
}
// get post info
const postId = req.query.topic;
const post = axios.get('https://inspiredformen.herokuapp.com/posts', postId);
console.log("post", post)
// getPostById(postId);
if(!post) return res.status(404).send("Post not found");
// inject meta tags
htmlData = htmlData.replace(
"<title>React App</title>",
`<title>${post.topic}</title>`
)
.replace('__META_OG_TITLE__', post.topic)
.replace('__META_OG_DESCRIPTION__', post.topic)
.replace('__META_DESCRIPTION__', post.topic)
// .replace('__META_OG_IMAGE__', post.thumbnail)
return res.send(htmlData);
});
});
// listening...
app.listen(PORT, (error) => {
if (error) {
return console.log('Error during app startup', error);
}
console.log("listening on " + PORT + "...");
});
here is index.html file
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title>__META_OG_TITLE__</title>
<meta name="description" content="__META_DESCRIPTION__"/>
<meta name="og:title" content="__META_OG_TITLE__"/>
<meta name="og:description" content="__META_OG_DESCRIPTION__"/>
<meta name="twitter:card" content="summary_large_image" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="theme-color" content="#000000" />
...
</head>
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
<div id="root"></div>
...
</body>
</html>
Please, I will appreciate if anyone can help me with to solve this problem.
Thanks all
I would like to embed this code on my Gatsby site, what would be the best practice? I'm still learning and any help would be much appreciated.
Thanks
<script
id="nc-booking-widget-script"
src="https://www.neatcal.com/app/services/booking/assets/js/integrate.js?v=21.04.26.5"
data-access-key="fcd3a2ae209d9ccae0d5fb3e66e9dabc"
data-nc-root="https://www.neatcal.com/app"
data-booking-url="https://www.neatcal.com/app/services/booking/book?u=fcd3a2ae209d9ccae0d5fb3e66e9dabc&widget_order=all_services,time&photo_mode=0&click_and_go=1&class_list_tmpl=list-1"></script>
The easiest and native approach, not only in Gatsby, but generally in React is using the Helmet component. Basically, whatever is wrapped inside this component is outputted in the <head> tag, no matter the original component.
To use it, just install (via yarn add react-helmet or npm install --save react-helmet) it and use like:
import React from "react";
import {Helmet} from "react-helmet";
const HomePage = (props)=> {
return (
<div className="application">
<Helmet>
<meta charSet="utf-8" />
<title>My Title</title>
<link rel="canonical" href="http://someSite.net/example" />
<script id="nc-booking-widget-script" src="https://www.neatcal.com/app/services/booking/assets/js/integrate.js?v=21.04.26.5" data-access-key="fcd3a2ae209d9ccae0d5fb3e66e9dabc" data-nc-root="https://www.neatcal.com/app" data-booking-url="https://www.neatcal.com/app/services/booking/book?u=fcd3a2ae209d9ccae0d5fb3e66e9dabc&widget_order=all_services,time&photo_mode=0&click_and_go=1&class_list_tmpl=list-1"/>
</Helmet>
...
</div>
);
};
In Gatsby, in addition, you can customize the resultant HTML (created once build) by:
cp .cache/default-html.js src/html.js
This will copy the default-html.js file from .cache to src/html.js. There, you can embed your script like:
import React from "react"
import PropTypes from "prop-types"
import React from "react"
import PropTypes from "prop-types"
export default function HTML(props) {
return (
<html {...props.htmlAttributes}>
<head>
<meta charSet="utf-8" />
<meta httpEquiv="x-ua-compatible" content="ie=edge" />
<meta
name="viewport"
content="width=device-width, initial-scale=1, shrink-to-fit=no"
/>
<script
id="nc-booking-widget-script"
src="https://www.neatcal.com/app/services/booking/assets/js/integrate.js?v=21.04.26.5"
data-access-key="fcd3a2ae209d9ccae0d5fb3e66e9dabc"
data-nc-root="https://www.neatcal.com/app"
data-booking-url="https://www.neatcal.com/app/services/booking/book?u=fcd3a2ae209d9ccae0d5fb3e66e9dabc&widget_order=all_services,time&photo_mode=0&click_and_go=1&class_list_tmpl=list-1"></script>
{props.headComponents}
</head>
<body {...props.bodyAttributes}>
{props.preBodyComponents}
<div
key={`body`}
id="___gatsby"
dangerouslySetInnerHTML={{ __html: props.body }}
/>
{props.postBodyComponents}
</body>
</html>
)
}
HTML.propTypes = {
htmlAttributes: PropTypes.object,
headComponents: PropTypes.array,
bodyAttributes: PropTypes.object,
preBodyComponents: PropTypes.array,
body: PropTypes.string,
postBodyComponents: PropTypes.array,
}
Both are valid and cleanest approaches.
I'm trying to set up SSR for my React app, which uses Apollo Client for graphql requests to an API. I've set up the webpack config, and the Express app, correctly, and I can see the components getting loaded. The issue here is that getDataFromTree is not waiting for my query at all. It simply returns the component with loading: true and then nothing. I set ssrForceFetchDelay as well to make the client refetch the queries, but nothing.
I keep getting window.__APOLLO_STATE__={}. The components are very basic, making simple queries and displaying the result.
This is my express app:
app.use((req, res) => {
const client = new ApolloClient({
ssrMode: true,
link: new HttpLink({
uri: 'http://localhost:8000/graphql',
fetch: fetch
}),
cache: new InMemoryCache(),
});
const app = (
<ApolloProvider client={client}>
<Router location={req.url} context={{}}>
<App client={client}/>
</Router>
</ApolloProvider>
);
const jsx = extractor.collectChunks(app); // Using loadable
renderToStringWithData(jsx).then((dataResp) => {
const content = dataResp;
const state = client.extract(); // Always {}
const helmet = Helmet.renderStatic();
const html = ReactDOMServer.renderToStaticMarkup(
<Html content={content} helmet={helmet} assets={assets} state={state} />,
);
res.status(200); res.send(`<!doctype html>${html}`);
res.end();
}).catch(err => {
console.log(`Error thrown`);
});
});
app.listen(port, () => {
console.log(`Server listening on ${port} port`);
});
This is my client-side Apollo config:
const cache = new InMemoryCache().restore(window.__APOLLO_STATE__);
const client = new ApolloClient({
ssrForceFetchDelay: 300,
link: links,
cache: cache,
connectToDevTools: true,
});
My App :
const Wrapped = () => {
console.log(`Inside Wrapped`);
return (
<ApolloProvider client={apolloClient}> //The client one
<Router>
<App /> /*Only returns 2 basic components */
</Router>
</ApolloProvider>
);
};
hydrate(<Wrapped />, document.getElementById('root'));
I can see the components on the page, but no queries have been loaded and APOLLO_STATE is always empty. I've been following the documentation on react-apollo to set this up, but I can't understand why this basic set up won't work. Any ideas on what I need to change here? Thanks!
Html component:
import React from 'react';
const Html = ({ content, helmet, assets, state }) => {
return (
<html lang="en">
<head>
<meta charSet="utf-8" />
<meta
name="viewport"
content="width=device-width, initial-scale=1, shrink-to-fit=no"
/>
<meta name="theme-color" content="#000000" />
<link rel="manifest" href="/manifest.json" />
<link rel="shortcut icon" href="/favicon.ico" />
{helmet.meta.toComponent()}
{helmet.title.toComponent()}
{assets.css &&
assets.css.map((c, idx) => (
<link key={idx} href={c} rel="stylesheet" />
))}
</head>
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
<div id="root" dangerouslySetInnerHTML={{ __html: content }} />
<script
dangerouslySetInnerHTML={{
__html: `window.__APOLLO_STATE__=${JSON.stringify(state).replace(
/</g,
'\\u003c',
)};`,
}}
/>
{assets.js &&
assets.js.map((j, idx) => (
<script key={idx} src={j} />
))}
</body>
</html>
);
};
export default Html;
EDIT: Added my HTML component code
Link to repo with basic code that is working correctly. I intend to add Loadable to this to see if that is what is causing the issue.
https://github.com/kvnam/gql-ssr-test