Authors:
Inna Ivashchuk
Volodymyr Vyshko
This is Googlebot
Crawler
Queue
Crawler
URL
Procesing
Index
Ranking
Render
Queue
Renderer
HTML
Rendered HTML
URLs
but not right away
<html>
<head>
<title>My super HTML</title>
</head>
<body>
<header>
<nav>
<ul><li><a href="/home">Home</a></li></ul>
</nav>
<h1>Page title</h1>
</header>
<main>
<article>Nice article can be there</article>
<section>
<form><input type="email" name="email"></form>
</section>
</main>
<footer> </footer>
</body>
</html>
<head>
<meta charset="UTF-8">
<meta name="description" content="Star Wars slides about SEO">
<meta name="keywords" content="HTML,CSS,SEO,JavaScript,Vader,Luke,R2D2">
<meta name="author" content="Master Yoda">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
</head>
The <meta> tag provides metadata about the HTML document. Metadata will not be displayed on the page, but will be machine parsable.
Meta elements are typically used to specify page description, keywords, author of the document, last modified, and other metadata.
The metadata can be used by browsers (how to display content or reload page), search engines (keywords), or other web services.
Tranditional Urls
Tricking the browser with fragment identifiers
Best practice? Use History API
<a href="/home" onClick={doSomething}>Home</a>
<h1>Characters in Star Wars</h1>
<p>
There is you can find the collection of the most powerful characters of Star Wars
</p>
<img src="/img/jedi.jpg" alt="Super powerful Jedi">
<img src="/img/sith.jpg" alt="Super powerful Sith Lord">
<!DOCTYPE html>
<html lang="en">
<head>
<title>StarWars | The Official Star Wars Website</title>
<link rel="stylesheet" href="https://static.disney.io/starwars.css" type="text/css">
</head>
<body>
<div id="content"></div>
<script src="https://static.disney.io/main.min.js"></script>
</body>
</html>
import React from 'react';
import { Helmet } from 'react-helmet';
class MainPage extends React.Component {
// ...
render() {
return (
<div>
// ...
<Helmet>
<title> unique, descriptive title </title>
<meta name="description" content="unique, helpful snippet" />
</Helmet>
</div>
)
}
}
export default MainPage;
Text
import express from 'express';
import React from 'react';
import ReactDOMServer from 'react-dom/server';
import { StaticRouter } from 'react-router';
import { Helmet } from 'react-helmet';
import { ServerStyleSheet } from 'styled-components';
import App from './src/app';
const sheet = new ServerStyleSheet();
const content = ReactDOMServer.renderToString(
sheet.collectStyles(
<StaticRouter location={req.url} context={{}}>
<App />
</StaticRouter>
)
);
const helmet = Helmet.renderStatic();
res.send(`
<html>
<header>
${helmet.title.toString()}
${helmet.meta.toString()}
${sheet.getStyleTags()}
</header>
<body>
<div id="root">
${content}
</div>
<script src="client_bundle.js"></script>
</body>
</html>
`)
const path = require('path');
module.exports = {
entry: './src/client.js',
output: {
filename: 'client_bundle.js',
path: path.resolve(__dirname,
'build/public'),
publicPath: '/build/public',
},
module: {
rules: [{
test: /\.(js|jsx)$/,
exclude: /node_modules/,
use: ['babel-loader']
}]
},
}
const path = require('path');
const webpackNodeExternals =
require('webpack-node-externals');
module.exports = {
target: 'node',
entry: './server.js',
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, 'build'),
publicPath: '/build',
},
module: {
rules: [{
test: /\.(js|jsx)$/,
exclude: /node_modules/,
use: ['babel-loader']
}]
},
externals: [webpackNodeExternals()]
}
Client
Server
"webpack:server": "webpack --config webpack.server.js --watch",
"webpack:client": "webpack --config webpack.client.js --watch",
"webpack:start": "nodemon --watch build exec node build/bundle.js",
"dev": "npm-run-all --parallel webpack:*"
package.json
nodemon - is a tool that helps develop node.js based applications by automatically restarting the node application when file changes in the directory are detected.
npm-run-all - a CLI tool to run multiple npm-scripts in parallel or sequential.
ReactDOM.hydrate(element, container[, callback])
export const botUserAgents = [
'googlebot',
'google-structured-data-testing-tool',
'bingbot',
'linkedinbot',
'mediapartners-google',
];
isPrerenderedUA = userAgent.matches(botUserAgents)
isMobileUA = userAgent.matches(['mobile', 'android'])
if (!isPrerenderedUA) {
...
} else {
servePreRendered(isMobileUA)
}
app.get('/', function(req, res) {
console.log('User-Agent: ' + req.headers['user-agent']);
});
Get user-agent:
Render only for bot:
import { renderToString } from 'react-dom/server'
function handleRender(req, res) {
// Create a new Redux store instance
const store = createStore(counterApp)
// Render the component to a string
const html = renderToString(
<Provider store={store}>
<App />
</Provider>
)
// Grab the initial state from our Redux store
const preloadedState = store.getState()
// Send the rendered page back to the client
res.send(renderFullPage(html, preloadedState))
}
renderFullPage(html, preloadedState) {
return `
<!doctype html>
<html>
<head>
<title>Redux Universal Example</title>
</head>
<body>
<div id="root">${html}</div>
<script>
// WARNING: See the following for security issues around embedding JSON in HTML:
// http://redux.js.org/recipes/ServerRendering.html#security-considerations
window.__PRELOADED_STATE__ = ${JSON.stringify(preloadedState).replace(
/</g,
'\\u003c'
)}
</script>
<script src="/static/bundle.js"></script>
</body>
</html>
`
}
import React from 'react'
import { hydrate } from 'react-dom'
import { createStore } from 'redux'
import { Provider } from 'react-redux'
import App from './containers/App'
import counterApp from './reducers'
// Grab the state from a global variable injected into the server-generated HTML
const preloadedState = window.__PRELOADED_STATE__
// Allow the passed state to be garbage-collected
delete window.__PRELOADED_STATE__
// Create Redux store with initial state
const store = createStore(counterApp, preloadedState)
hydrate(
<Provider store={store}>
<App />
</Provider>,
document.getElementById('root')
)
Gatsby, next.js - Two of the most popular solutions that provide SSR for React
Demo project source code:
Presentation can be found: