Reputation: 11
I am working on a react-based project which has a facebook feed kind of interface. My frontend is based on React and backend is on serverless, aws-sdk framework.
When clicks on a post and share it on social media,I need to generate previews dynamically based on the post data. I have seen that SSR is a solution, but all my project is based on CSR and I have no option to go to SSR now. Also, since the data is large, latency will get affected.
How to resolve this?
I tried using react-helmet and react-helmet-async. They are modifying the meta tags in the index.html, but still the previews are not generated based on the dynamic data. I also tried to dynamically modify the index.html using a function in the tag. That also did not work.
Upvotes: 1
Views: 327
Reputation: 61
Social media apps detect urls and parse metadata in the html. it is generally inside meta tags. Since CSR frameworks render the app on static root html using javascript at the browser, you should somehow create an html that includes meta tags with content for that specific url you want to share. Here is how:
Instead of serving your app as static file, create a backend app (like using nodejs)
serve your frontend app from build folder
create a route that will handle your preview url (such as /profile/:id , /course/:id ) inside this handler, read static index.html from build folder, inject necessary metatags into html (you may need to fetch data from api) then return html.
Nodejs example:
const express = require('express');
const path = require('path');
const fetch = require('node-fetch');
const fs = require('fs');
const app = express();
const PORT = 3000;
// Serve static files from the React app's build folder
app.use(express.static(path.join(__dirname, 'build')));
// Route to handle profile page
app.get('/profile/:id', async (req, res) => {
const profileId = req.params.id;
const apiUrl = `https://api.example.com/profile/${profileId}`;
try {
// Fetch profile details from an external API
const response = await fetch(apiUrl);
if (!response.ok) {
throw new Error('Failed to fetch profile details');
}
const profile = await response.json();
// Read the base index.html file
const htmlFile = path.join(__dirname, 'build', 'index.html');
let html = fs.readFileSync(htmlFile, 'utf8');
// Inject Open Graph, Twitter, and other meta tags
const metaTags = `
<meta property="og:title" content="${profile.name}" />
<meta property="og:description" content="${profile.bio}" />
<meta property="og:image" content="${profile.image}" />
<meta property="og:url" content="https://example.com/profile/${profileId}" />
<meta name="twitter:card" content="summary_large_image" />
<meta name="twitter:title" content="${profile.name}" />
<meta name="twitter:description" content="${profile.bio}" />
<meta name="twitter:image" content="${profile.image}" />
`;
// Inject the meta tags into the <head> section of the HTML
html = html.replace(
'<head>',
`<head>${metaTags}`
);
// Send the modified HTML
res.send(html);
} catch (error) {
console.error('Error fetching profile:', error.message);
// If there's an error, send the raw index.html
const htmlFile = path.join(__dirname, 'build', 'index.html');
res.sendFile(htmlFile);
}
});
// Fallback for other routes to serve index.html (for React Router)
app.get('*', (req, res) => {
res.sendFile(path.join(__dirname, 'build', 'index.html'));
});
// Start the server
app.listen(PORT, () => {
console.log(`Server running on http://localhost:${PORT}`);
});
Upvotes: 0