create-react-app for server-side rendering - reactjs

Im using create-react-app tool for building my first react application with react-routes and now I would like to use server side rendering to avoid loading all pages at once.
I followed guides and installed express.js, separated client-side and server-side with .js files and run it with
NODE_ENV=production babel-node --presets 'react,es2015' src/server.js
But I get an error when app is trying to compile sass #import statements. I think I have to serve assets first, but I don't know how to insert webpack functions in server.js logic
create-react-app also has npm run build command for production build and create js and css files, so maybe there is some way to skip assets parts while compiling server.js ?
Server.js file contents
import path from 'path';
import { Server } from 'http';
import Express from 'express';
import React from 'react';
import { renderToString } from 'react-dom/server';
import { match, RouterContext } from 'react-router';
import routes from './routes';
import NoMatch from './pages/NoMatch';
// initialize the server and configure support for ejs templates
const app = new Express();
const server = new Server(app);
app.set('view engine', 'ejs');
app.set('views', path.join(__dirname, 'views'));
// define the folder that will be used for static assets
app.use(Express.static(path.join(__dirname, 'static')));
// universal routing and rendering
app.get('*', (req, res) => {
match(
{ routes, location: req.url },
(err, redirectLocation, renderProps) => {
// in case of error display the error message
if (err) {
return res.status(500).send(err.message);
}
// in case of redirect propagate the redirect to the browser
if (redirectLocation) {
return res.redirect(302, redirectLocation.pathname + redirectLocation.search);
}
// generate the React markup for the current route
let markup;
if (renderProps) {
// if the current route matched we have renderProps
markup = renderToString(<RouterContext {...renderProps}/>);
} else {
// otherwise we can render a 404 page
markup = renderToString(<NoMatch/>);
res.status(404);
}
// render the index template with the embedded React markup
return res.render('index', { markup });
}
);
});
// start the server
const port = process.env.PORT || 3000;
const env = process.env.NODE_ENV || 'production';
server.listen(port, err => {
if (err) {
return console.error(err);
}
console.info(`Server running on http://localhost:${port} [${env}]`);
});

Try installing node-sass and following the official guide
https://create-react-app.dev/docs/adding-a-sass-stylesheet/

Related

React App with basename not working in production build, but works in development (create-react-app)

I have created an app using create-react-app with a basename '/admin' and it is working just fine in development mode. All routes working properly both on localhost and behind nginx proxy.
When I build the app using npm run build I get a blank screen on the '/admin' url, with the following errors in the console:
The script from “https://192.168.1.2/admin/static/js/main.49bb4878.js”
was loaded even though its MIME type (“text/html”) is not a valid
JavaScript MIME type.
The stylesheet https://192.168.1.2/admin/static/css/main.4efb37a3.css
was not loaded because its MIME type, “text/html”, is not “text/css”.
Uncaught SyntaxError: expected expression, got '<' main.49bb4878.js:1
I have tried both <BrowserRouter pathname="/admin">...</BrowserRouter> and the one I have in the following index.js file.
It seems like the server sends the index.html file no matter what the client requests...
This is my index.js file:
import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';
import { Provider } from 'react-redux';
import { configureStore } from '#reduxjs/toolkit';
import storeToConfigure from './configureStore';
import CustomRouter from './utils/CustomRouter';
import * as buffer from 'buffer';
import { BrowserRouter } from 'react-router-dom';
console.log(window);
window.Buffer = buffer;
window.process = {}
export const store = configureStore(storeToConfigure);
export const history = createBrowserHistory({ basename: '/admin' });
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<Provider store={store}>
<CustomRouter history={history} basename="/thug-app">
<ScrollToTop>
<App />
</ScrollToTop>
</CustomRouter>
</Provider>
);
// 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(console.log);
This is the CustomRouter I'm using, in order to be able to directly access the history anywhere without a hook:
import React, { useLayoutEffect, useState } from 'react';
import { Router } from 'react-router-dom';
const CustomRouter = ({
basename,
children,
history,
}) => {
const [state, setState] = useState({
action: history.action,
location: history.location,
});
useLayoutEffect(() => history.listen(setState), [history]);
return (
<Router
basename={basename}
children={children}
location={state.location}
navigationType={state.action}
navigator={history}
/>
);
};
export default CustomRouter;
Again, everything works just fine in development. The problem is when I build the app for production. I have tried both pm2 and serve packages (same names on npmjs). The serve package returns 404, while pm2 returns the errors I mention above.
Thank you for taking the time to help!
I managed to make it work with this workaround:
I ditched both pm2 and serve and used a custom express.js server.
The setup:
const express = require('express');
const path = require('path');
const fs = require('fs');
const app = express();
// app.use(express.static(path.join(__dirname, 'build')));
app.get('/*', (req, res) => {
let theUrl = req.originalUrl.replace('/admin', '');
if (!!!theUrl) {
theUrl = 'index.html';
}
try {
const target = path.join(__dirname, 'build', theUrl);
console.log(target);
if (fs.statSync(target)) {
res.sendFile(target);
} else {
res.sendFile(path.join(__dirname, 'build', 'index.html'));
}
} catch (error) {
console.log(error);
res.sendFile(path.join(__dirname, 'build', 'index.html'));
}
});
app.listen(4000, () => console.log('admin app listening on 4000'));
It also seems to work fine without the following line:
app.use(express.static(path.join(__dirname, 'build')));
Like I had noticed, both pm2 and serve sent the index.html file no matter what the client requested.
Seems to be working fine behind nginx proxy as well.
Whenever the requested file does not exist, simply serve the index.js file.

Cannot Render images in React while using Server Side Rendering after creating Build ,Cannot GET /[object%20Object]

I used create-react-app to create a React project. I was experimenting with server-side rendering on express servers.
I found a code snippet that showed me how to do it.
ReactDOM.hydrate(
<React.StrictMode>
<App />
</React.StrictMode>,
document.getElementById("root")
);
This is my server side code.
index.js
require("ignore-styles");
require("#babel/register")({
ignore: [/(node_modules)/],
presets: ["#babel/preset-env", "#babel/preset-react"],
});
require("./server");
server.js
import path from "path";
import fs from "fs";
import express from "express";
import React from "react";
import ReactDOMServer from "react-dom/server";
import App from "../src/App";
const PORT = 8080;
const app = express();
const router = express.Router();
const serverRenderer = (req, res, next) => {
fs.readFile(path.resolve("./build/index.html"), "utf8", (err, data) => {
if (err) {
console.error(err);
return res.status(500).send("An error occurred");
}
return res.send(
data.replace(
'<div id="root"></div>',
`<div id="root">${ReactDOMServer.renderToString(<App />)}</div>`
)
);
});
};
router.use("^/$", serverRenderer);
router.use(
express.static(path.resolve(__dirname, "..", "build"), { maxAge: "30d" })
);
// tell the app to use the above rules
app.use(router);
// app.use(express.static('./build'))
app.listen(PORT, () => {
console.log(`SSR running on port ${PORT}`);
});
It works fine when I try to get text or other data; however, when I try to render images from the server side, they do not appear.
I tried every other solution I could think of, but nothing seemed to work.
images are not rendering
and also not showing in View Page Source
enter image description here
we also tried <img src={require("./logo.svg")} className="App-logo" alt="logo" />
I tried all the solutions from stackoverflow as well as other sources, but its not working.
I was expecting images to be rendered properly on page.

Problem with the build in react js, can't find the build folder

I'm trying to use react, serverless and SSR please don't ask me why I don't use next.js, anyway.
i got the next error ERROR in unable to locate '/home/franklinserif/proyects/ssr-develop/build' glob
here is my server index.js code
// index.js
import serverless from "serverless-http";
import express from "express";
import path from "path";
// import middleware
import renderer from "./middleware/renderer";
const app = express();
// root (/) should always serve our server rendered page
app.use("^/$", renderer);
// serve static assets
console.log(path.join(__dirname, "./client", "./build"));
app.use(express.static(path.join(__dirname, "./client", "./build")));
// handler
export const handler = serverless(app);
and this is my renderer file
// renderer.js
import fs from "fs";
import path from "path";
import React from "react";
import ReactDOMServer from "react-dom/server";
// import main App component
import App from "../client/src/App";
export default (req, res, next) => {
// point build index.html
console.log(path.resolve("../client", "./build", "index.html"));
const filePath = path.resolve("../client", "./build", "index.html");
// read in html file
fs.readFile(filePath, "utf8", (err, htmlData) => {
if (err) {
return res.send(err).end();
}
// render the app as a string
const html = ReactDOMServer.renderToString(<App />);
// inject the rendered app into our html and send it
return res.send(
// replace default html with rendered html
htmlData.replace('<div id="root"></div>', `<div id="root">${html}</div>`)
);
});
};
I don't know why I can find the build folder

I am trying to implement server side rendering(ssr) in my react website, but I faced an error while running the express server?

I am trying to implement server side rendering(ssr) in my react website, but I faced an error
C:\Users\RONAK ASNANI\Desktop\storeshop\src\images\bookappointment.webp:1
RIFF�,
SyntaxError: Invalid or unexpected token
So, do I need to configure a webpack for server side redering and if yes then how?
Currently I just have a server folder setup in the root directory having server.js and index.js file.
require("ignore-styles");
require("#babel/register")({
ignore: [/(node_modules)/],
presets: ["#babel/preset-env", "#babel/preset-react"],
});
require("./server");
This is index.js
import express from "express";
import fs from "fs";
import path from "path";
import React from "react";
import ReactDOMServer from "react-dom/server";
import App from "../src/App";
const PORT = 8000;
const app = express();
app.use("^/$", (req, res, next) => {
fs.readFile(path.resolve("./build/index.html"), "utf-8", (err, data) => {
if (err) {
console.log(err);
return res.status(500).send("Some error happened");
}
return res.send(
data.replace(
'<div id="root"></div>',
`<div id="root">${ReactDOMServer.renderToString(<App />)}</div>`
)
);
});
});
app.use(express.static(path.resolve(__dirname, "..", "build")));
app.listen(PORT, () => {
console.log(`App launched on ${PORT}`);
});
This is server.js
I am totally new to server side rendering so any suggestions or guidance is welcomed?
If you want to ssr React yourself you can config webpack for development and production and for each we have client and server name , you can find samples in gitub , my suggestion is read from github first and get good prespective and after that start that ,
for example : https://github.com/cullenjett/react-ssr-boilerplate

Web3 with Webpack Build not found without webpack-dev-server

With a Reactjs webpack project I am able to run webpack dev server and access my index.html with web3 picked up.
If I build the project and open the index.html in Chrome then web3 is not detected.
Everything works when running webpack-dev-server --mode development --open --hot
but with webpack --mode development then web3 is not injected
The purpose of my app is a tool to be run locally, it does not have to be served from anywhere public, also I don't see that I need to run a lite server to serve the content.
web3: 1.0.0-beta.36
webpack: 4.22.0
webpack-cli: 3.1.2
webpack-dev-server: 3.1.8
import './index.css';
import IxoTimelockApp from './components/IxoTimelockApp';
import InstallMetaMask from './components/install-
metamask/install-metamask-component.jsx';
let regeneratorRuntime = require("regenerator-runtime");
class App extends Component {
state = {
web3Obj:null
}
componentDidUpdate(prevprops) {
if (prevprops != this.props){
this.setState({web3Obj: this.props.web3Obj})
}
}
componentDidMount(){
window.addEventListener('load', async () => {
// Modern dapp browsers...
if (window.ethereum) {
window.web3 = new Web3(ethereum);
try {
// Request account access if needed
await ethereum.enable();
this.setState({web3Obj: window.web3})
} catch (error) {
// User denied account access...
}
}
// Legacy dapp browsers...
else if (window.web3) {
window.web3 = new Web3(web3.currentProvider);
this.setState({web3Obj: window.web3})
}
// Non-dapp browsers...
else {
console.log('Non-Ethereum browser detected. You should consider trying MetaMask!');
}
});
}
render() {
if(this.state.web3Obj) {
return <TimelockApp/>
}else return <InstallMetaMask/>
}
}
export default App;
const wrapper = document.getElementById("root");
wrapper ? ReactDOM.render(<App />, wrapper) : false;
From: MetaMask Compatibility Guide
Requirements 🔩
🌐 Http(s) - Web Server Required
Due to browser security restrictions, we can't communicate with dapps
running on file://. Please use a local server for development.

Resources