Using react-snap for SEO - reactjs

I'm using react snap for SEO.
Here's what I did.
I added the following to index.js in my react application
import React from 'react';
import { hydrate, render } from "react-dom";
const rootElement = document.getElementById("root");
if (rootElement.hasChildNodes()) {
hydrate(<App />, rootElement);
} else {
render(<App />, rootElement);
}
Then I added the following to package.json
"postbuild": "react-snap"
When I run npm run build, and do View Source on the pages, I don't see the real time meta description and title that I need for the page.
What am I doing wrong?
Here's how I set the meta tags.
In BookDetail.jsx
import {Helmet} from "react-helmet";
<Helmet>
<meta charSet="utf-8" />
<title>{this.state.book.title} by {this.state.book.author} | Sumizeit</title>
<meta name="description" content={this.state.book.desc}/>
</Helmet>

do not use react-snap just change to next.js (react-snap so many problems: refresh error on protected routes, build time no joke, inline css)...by the way there is nothing wrong in your code

Related

Problemn with MultiPage app that adds react widget, how to add it to other pages not only the index.html

I built a normal HTML/JS/CSS website and am Adding a widget( a code form made with react ) that I compile and export as a js and css to the index.html page by doing so:
<head>
<script src="https://unpkg.com/react#16.13.1/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom#16.13.1/umd/react-dom.development.js"></script>
</head>
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
<div id="app"></div>
<link href="app.css" rel="stylesheet"/>
<script src="app.js"></script>
</body>
And I"m compiling the app with:
//yarn parcel , or npm run parcel
"parcel": "parcel build src/index.js --no-minify --no-source-maps -d widget && cp build/static/css/*.css widget/index.css",
React js script that get's the component and then add it to the element with id.
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';
const app = document.getElementById('app')
ReactDOM.render(
(<React.StrictMode>
<App />
</React.StrictMode>),
app
);
After compiling I tried to make a secondpage serving it in another route, like blog.html and add the component too as I did with head and body, but the page remains blank. What could it be ? ( there are no logs in the console).
//the first html served
files_routes.get('/', (request, response) => {
response.sendFile('index.html', { root: '../build' })
});
//the second html which doesn't work
files_routes.get('/blog', (request, response) => {
response.sendFile('blog.html', { root: '../build' })
});
I couldn't replicate the issue in the newer code base. So I'll be closing this question...

React test fails becasue target container is not a DOM element

I'm trying to run component tests and the tester keeps on failing because the target container is not a DOM element. I found an answer that recommended removing exported values from index.tsx but it did not fix the issue.
src/Component/__tests__/FirstPage.test.tsx
● Test suite failed to run
Target container is not a DOM element.
8 | export const socket = io('http://localhost:8080');
9 |
> 10 | ReactDOM.render(
| ^
11 | <React.StrictMode>
12 | <App/>
13 | </React.StrictMode>,
at Object.render (node_modules/react-dom/cjs/react-dom.development.js:26091:13)
Here is my App.tsx file
import React, { Component } from "react";
import './App.css';
import FirstPage from './Component/FirstPage/FirstPage';
export default class App extends Component {
render() {
return (
<FirstPage/>
);
}
}
Here is the index.tsx file:
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import io from 'socket.io-client';
import App from './App';
import reportWebVitals from './reportWebVitals';
export const socket = io('http://localhost:8080');
ReactDOM.render(
<React.StrictMode>
<App/>
</React.StrictMode>,
document.getElementById('root')
);
// If you want to start measuring performance in your app, pass a function
// to log results (for example: reportWebVitals(console.log))
// or send to an analytics endpoint
reportWebVitals();
And here is the test
import { render, screen } from '#testing-library/react';
import userEvent from '#testing-library/user-event';
import React from 'react';
import FirstPage from '../FirstPage/FirstPage';
import User from '../User/User';
const testUser = new User("testUser", "id")
test('Test if FirstPage includes enter lobby button', () => {
render(<FirstPage/>);
const textElement= screen.getByText(/Enter Lobby!s/i);
expect(textElement).toBeInTheDocument();
});
Here is my index.html:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8"/>
<link rel="shortcut icon" href="%PUBLIC_URL%/favicon.ico"/>
<meta
name="viewport"
content="width=device-width, initial-scale=1, shrink-to-fit=no"
/>
<meta name="theme-color" content="#000000"/>
<!--
Notice the use of %PUBLIC_URL% in the tags above.
It will be replaced with the URL of the `public` folder during the build.
Only files inside the `public` folder can be referenced from the HTML.
Unlike "/favicon.ico" or "favicon.ico", "%PUBLIC_URL%/favicon.ico" will
work correctly both with client-side routing and a non-root public URL.
Learn how to configure a non-root public URL by running `npm run build`.
-->
<title>React App</title>
</head>
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
<div id="root"></div>
<!--
This HTML file is a template.
If you open it directly in the browser, you will see an empty page.
You can add webfonts, meta tags, or analytics to this file.
The build step will place the bundled scripts into the <body> tag.
To begin the development, run `npm start` or `yarn start`.
To create a production bundle, use `npm run build` or `yarn build`.
-->
</body>
</html>
Edit
After spending some time on your project I found out what was causing this issue with your tests. In some of the components that FirstPage is using, you were importing socket like that
import { socket } from "../.."
This caused unexpected behaviour in the tests because the path was wrong.
To fix this, I took the socket export from index.tsx and put it in App.tsx and wherever you need to import it you can just do lit like this
import { socket } from "../../App"
That resolves the issues with the tests. Also, the improvements mention above still apply for cleaner code .
First of all, in your App.tsx you are importing FirstPage and name it as Greeting but then you are returning FirstPage in your component. You need to either return Greeting or rename the import to FirstPage. Also, you need to import React in order for the code to work. Even if you don't use React explicitly, you still need to import it because JSX is tranpiled to React.createElement() which uses React.
// App.tsx
import React, { Component } from "react";
import FirstPage from "./Component/FirstPage/FirstPage";
export default class App extends Component {
render() {
return <FirstPage />;
}
}
Moving on to your test, it's a good practice to put all the tests inside __tests__ folder. Also, it looks like you don't actually import FirstPage as well and then try to use it. Try importing it and running the tests again with the proposed changes in the App.tsx.
// /Components/FirstPage/__tests__/FirstPage.test.tsx
import React from "react";
import { render, screen } from "#testing-library/react";
import FirstPage from '../FirstPage';
test("Test if FirstPage includes enter lobby button", () => {
render(<FirstPage />);
...
});
I've tried it in the codesandbox and the tests worked.

React helmet or next/head for Next Js project?

I am making a next js application (React SSR), and now I am into implementing the meta tags in head.
So for now I have used next/head in _app.tsx file like,
import React from 'react';
import App from 'next/app';
import Head from 'next/head';
import { ThemeProvider } from '#material-ui/core/styles';
import CssBaseline from '#material-ui/core/CssBaseline';
import theme from '../src/theme';
export default class MyApp extends App {
componentDidMount() {
// Remove the server-side injected CSS.
const jssStyles = document.querySelector('#jss-server-side');
if (jssStyles) {
jssStyles.parentElement!.removeChild(jssStyles);
}
}
render() {
const { Component, pageProps } = this.props;
return (
<React.Fragment>
<Head>
<title>My page</title>
<meta name="viewport" content="minimum-scale=1, initial-scale=1, width=device-width" />
</Head>
<ThemeProvider theme={theme}>
{/* CssBaseline kickstart an elegant, consistent, and simple baseline to build upon. */}
<CssBaseline />
<Component {...pageProps} />
</ThemeProvider>
</React.Fragment>
);
}
}
And the whole working code can be found here at sandbox: https://codesandbox.io/s/interesting-tereshkova-217ks
I am just to know whether using next/head itself enough in next js application or else need to implement react-helmet ??
If the react-helmet is needed then kindly help me how to implement in the provided next js application.
I am new into Next Js and SSR, So please help me in right direction which is the best method to achieve the result.
I am just to know whether using next/head itself enough in next js application or else need to implement react-helmet ??
react-helmet makes sense to use if you're rolling your own server side rendering solution and are not using Next.js. As far as I know, next/head is bascially a built-in version of react-helmet and does everything react-helmet does.
So no, you don't need to use react-helmet if you are using Next.js. Just use next/head.
If the react-helmet is needed then kindly help me how to implement in the provided next js application.
That said, if you want to use react-helmet with Next.js, here is an example: https://github.com/zeit/next.js/tree/canary/examples/with-react-helmet. Not sure why you'd do this, but the example exists. There is some discussion about it.
By the way, just like react-helmet, you can use next/head anywhere in your render tree—not just the App component like in your example—and all the tags will be aggregated into the <head> tag at the top.
There is also a great package for Meta tags for Next JS
next-seo

React Meta Tags Server Side

I have completed the meta tags implementation in react js at client side using react document meta and my title has changed.However I am confused about the server side prerendering for meta tags.I am completely new at this.I have gone through links like React-document-data and Npm React-document-meta but could not get through server side rendering.Would appreciate any help
Ok I've been doing this recently with expressjs and I think I've cracked it!
Firstly you're going to want to make one singular instance of your react 'app' which will be babel-ified. I tend to stick everything in an src and babel it to dist. My main for the server then ends up being /dist/index.js and this includes my app using `dist/App/index.js.
Next you're going to want to make your assets because rendering locally within a node process is very different to on a DOM.
Ok so firstly src/index.js
import express from 'express';
import app from './App';
import Mailgun from 'mailgun-js';
const server = express();
server.use(express.static('public'));
const port = process.env.SERVER_PORT || 3000;
server.get('/*', function(request, response) {
response.send(app());
});
This is my App/index.js file which is my initial content not my react app!
import * as assets from './../Assets';
import App from './app';
import React from 'react';
import {renderToString} from 'react-dom/server';
import {Provider} from 'react-redux';
import createStore from './Store';
import reducers from './Reducers';
import sagas from './Sagas';
//const browserHistory = createBrowserHistory();
const store = createStore({}, reducers, sagas);
const html = renderToString(
<Provider store={store}>
<App />
</Provider>
);
export default function render () {
return `<!doctype html>
<html prefix="og: http://ogp.me/ns#">
<head lang="en">
<meta charset="UTF-8">
<title>Preloaded title</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
</head>
<body>
<link rel="stylesheet" href="build/${assets.get('index.css')}"/>
<div id="root">${html}</div>
<div id="fb-root"></div>
<script type="text/javascript">
// WARNING: See the following for security issues around embedding JSON in HTML:
// http://redux.js.org/docs/recipes/ServerRendering.html#security-considerations
window.__PRELOADED_STATE__ = ${JSON.stringify(store.getState()).replace(/</g, '\\u003c')}
</script>
<script type="text/javascript" src="build/${assets.get('index.js')}"></script>
</body>
</html>`;
}
the assets.get() function is essentially returning a full url to my assets based on a manifest file. Ignore this for now. Not particularly important.
That's essentially it for pre-rendering. Now you'll need a different version for the build js for rehydrating your react app.
So assets/js/index.js
import React from 'react';
import ReactDOM from 'react-dom';
import { Provider } from 'react-redux'
import createStore from './../../src/App/Store';
import {createBrowserHistory} from 'history';
import reducers from './../../src/App/Reducers';
import sagas from './../../src/App/Sagas';
import App from './../../src/App/app';
const preloadedState = window.__PRELOADED_STATE__;
// Allow the passed state to be garbage-collected
delete window.__PRELOADED_STATE__;
preloadedState.server.env = false;
const browserHistory = createBrowserHistory();
const store = createStore(browserHistory, reducers, sagas, preloadedState);
ReactDOM.hydrate(
<Provider store={store}>
<App />
</Provider>,
document.getElementById('root'));
notice ReactDOM.hydrate not ReactDOM.render.
In this file you can add your google analytic etc. Anything to do with the DOM/window, DOM load etc.
And that's essentially it! I used the below example to work from.
https://redux.js.org/docs/recipes/ServerRendering.html

Material-ui React components not working

I am trying to use http://www.material-ui.com/ for front end design. I am trying to execute the first example within the getting started tutorial. I have following html file :
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>examples</title>
</head>
<body>
<div id="app"></div>
<script type="text/javascript" src="App.js"></script>
<script type="text/javascript" src="MyAwesomeReactComponent.js"></script>
</body>
</html>
and following App.js and MyAwesomeReactComponent.js files as defined in ( http://www.material-ui.com/#/get-started/usage )
App.js :
import React from 'react';
import ReactDOM from 'react-dom';
import MuiThemeProvider from 'material-ui/styles/MuiThemeProvider';
import MyAwesomeReactComponent from './MyAwesomeReactComponent';
const App = () => (
<MuiThemeProvider>
<MyAwesomeReactComponent />
</MuiThemeProvider>
);
ReactDOM.render(
<App />,
document.getElementById('app')
);
MyAwesomeReactComponent.js:
import React from 'react';
import RaisedButton from 'material-ui/RaisedButton';
const MyAwesomeReactComponent = () => (
<RaisedButton label="Default" />
);
export default MyAwesomeReactComponent;
But when I run the index file, I can't see any output.
The problem is here since you are implementing ES6 code, you need to use a javascript compiler like babel to transform your ES6 code to ES5 which is current javascript standard that is supported by browsers.
It would be a good practice to use babel with webpack as a loader.
Following React's get started page's instruction is a good starting point. I'd suggest reading the instructions carefully.
Finally, if you want to use pure ES6 without transforming, the compatibility table is available here: https://kangax.github.io/compat-table/es6/

Resources