Is it possible to do server side rendering only to <head></head>
data? I am trying to use feature like next.js:
export async function getServerSideProps(context) {
return {
props: {}, // will be passed to the page component as props
}
}
So, in the express server it will catch the exported function getServerSideProps and set the data in the <head>
tag. I don't want every other features of next.js and thus I don't want to use next.js. Is it even possible?
In order to server-side render react app without using any SSR framework (next.js,remix.run etc), you have to change the architecture of your app. Because in a single-page application (SPA), browser downloads the empty HTML, parses it and when sees the script, it makes another request to download the bundled javascript code.
To create a function like getServerSideProps
you have to write a logic to tell your app that before you serve any page, make sure if there is a specific named function (you define it) run this function first. For this, you need to configure your react router differently. Configuration of react-router-route-config . you could also use different npm packages but finding good documentation would be hard
To learn more about how server-side react app is implemented please read this in-depth article react-router-server-rendering .
after this, I will quote from the article above to show you how to render only the head
import Home from "./Home";
import Grid from "./Grid";
import { fetchPopularRepos } from "./api";
const routes = [
{
path: "/",
component: Home,
},
{
path: "/popular/:id",
component: Grid,
// this is like getServerSideprops function.
// whatever logic is
fetchInitialDataCustomFunction: (path = "") => fetchPopularRepos(path.split("/").pop()),
},
];
export default routes;
server/index.js
import { matchPath } from "react-router-dom"
import routes from '../shared/routes'
app.get("*", (req, res, next) => {
const activeRoute = routes.find((route) =>
matchPath(route.path, req.url)
) || {}
})
Now, activeRoute will be the route of whatever page the user was requesting (req.url).
The next step is to see if that route requires any data. We'll check if the activeRoute has a fetchInitialData property. If it does, we'll invoke it passing it the current path, if it doesn't, we'll just continue on.
Now this is the part where you will implement to send only head
app.get("*", (req, res, next) => {
const activeRoute =
routes.find((route) => matchPath(route.path, req.url)) || {};
const promise = activeRoute.fetchInitialData
? activeRoute.fetchInitialData(req.path)
: Promise.resolve();
promise
.then((data) => {
const markup = ReactDOM.renderToString(<App serverData={data} />);
res.send(`
<!DOCTYPE html>
<html>
// this is the head part that you want to implement
<head>
<title>SSR with React Router</title>
<script src="/bundle.js" defer></script>
<link href="/main.css" rel="stylesheet">
------------------------------------------------
// this part is usually used for redux.
// we send the data store to the client, so the client will use it as the initial state of the app
<script>
window.__INITIAL_DATA__ = ${serialize(data)}
</script>
</head>
// you do not want this part. this is the main content is sent to the client
<body>
<div id="app">${markup}</div>
</body>
</html>
`);
})
.catch(next);
});
Something you could try is using a library like cheerio
or a headless browser like Puppeteer
to parse the HTML and extract only the head element. Below is an example using cheerio
:
import cheerio from 'cheerio';
export async function getServerSideProps(context) {
// Load the page HTML
const res = await fetch(`http://localhost:${process.env.PORT}${context.pathname}`);
const html = await res.text();
// Use cheerio to parse the HTML and extract the head element
const $ = cheerio.load(html);
const headHtml = $('head').html();
return {
props: {
headHtml,
},
};
}
I'm not sure if this is exactly what you were looking for, but if so I hope it helps.
Question: Is it possible to do server-side rendering only to data?
by using server-side rendering technology, such as Node.js and Express.
Follow these steps to achieve this:-
Create a route in your Express server that will handle the request for the page with the <head>
element you want to render.
install ejs npm install ejs
In the route handler, use the render method of your view engine (eg EJS, Pug, etc.)
to render a template with the <head>
element and any other data you want to include in it.
use placeholders (eg <%= title %>)
for the dynamic data that will be passed to the template from the route handler.
install packages:-
npm i express ejs cors
Server code:-
server.js
const ejs = require('ejs');
const express = require ('express');
const cors = require ('cors');
const app = express ();
const PORT = process.env.PORT || 3000
app.use(
cors({
origin: '*',
credentials: false,
methods: ['GET'],
}),
);
app.get('/head', (req, res) => {
// you can use data from database
const data = {
title: 'My Page',
description: 'This is my page.'
}
res.send(ejs.render(`
<head>
<title><%= title %></title>
<meta name="description" content="<%= description %>">
</head>
`, data))
})
app.listen(PORT, () => {
console.log('Listening on port 3000');
});
Here is example using React-js
install packages:-
npm i axios-hooks
Since OP
wants to use server-side rendering only on the head
that's why I'm using conditional rendering to render the page after the server response. You can aslo use server-side fetching
.
App.tsx
import useAxios from 'axios-hooks'
export default App () {
const [{ data, loading, error }, refetch] = useAxios(
'https://your-api/head'
);
return (
<>
{(!data && loading) && <>loading...</>}
{data ? <>App</> : <>error 404</>}
</>
);
}
next.js will indeed let you do that with the pre-rendering.
On every page, you can return only the <head/>
without <body/>
if you want, as shown here https://stackoverflow.com/a/60398652/18364469
As rendering may be quite complex, If you want a production ready solution, I suggest you to use next.js, they have tackled many issues you may not be aware of. If you don't want to use it, what are your constraints?
If you're just curious and want to re-create something similar to next.js, I suggest you reading the code on next.js, maybe starting from this file https://github.com/vercel/next.js/blob/canary/packages/next/server/next-server.ts as you're interested in server developement.
You can also setup a simple express server that returns an html document only with a <head/>
tag like this.
const express = require('express')
const app = express()
const port = 3000
app.get('/', (req, res) => {
res.send('<html><head></head></html>')
})
app.listen(port, () => {
console.log(`App listening on port ${port}`)
})
Feel free to provide more context to get a more accurate answer
Yes, it is possible to do server-side rendering only for the data in the tag. One way to do this is to use a library like React Helmet, which allows you to easily set the data in the tag from your React components.
Here is an example of how you could use React Helmet to set the title and description in the tag:
import { Helmet } from 'react-helmet';
const MyComponent = () => {
return (
<div>
<Helmet>
<title>My Page Title</title>
<meta name="description" content="My page description" />
</Helmet>
{/* Other component content goes here */}
</div>
);
}
Then, in your express server, you can use a library like ReactDOMServer to render the React component to a string, and insert the resulting HTML into the tag of your server-side rendered page.
For example:
import ReactDOMServer from 'react-dom/server';
app.get('/', (req, res) => {
const html = ReactDOMServer.renderToString(<MyComponent />);
res.send( <!DOCTYPE html> <html> <head> ${html} </head> <body> {/* Other page content goes here */} </body> </html> );
});
This approach allows you to use server-side rendering only for the data in the tag, without needing to use a full-featured framework like Next.js.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.