Hello, thanks to give me a star for this project.
React App with SSR Server Side Rendering. Webpack 5 installed manually. In dev mode we use live reload thanks to webpack-dev-middleware & webpack-hot-middleware modules.
Main modules used are @reduxjs/toolkit, @loadable/component, react-refresh, react-jss and typescript.
This project is accompanied with a free and easy to use CI/CD with Github Actions and Scaleway.
Checkout this app in live https://reacteasyssrjckf9fbl-reacteasyssrfront.functions.fnc.fr-par.scw.cloud (this is a free sleepy server, around 20 seconds to wake up)
This project is about Server-Side Rendering (SSR). Building an isomorphic application that runs on both the server and the client is not an easy task. This boilerplate repository helps simplify and streamline that process.
Clone the repo
git clone https://github.com/tomtom94/react-easy-ssr.git
cd react-easy-ssr
Node.js version v23.5.0 minimum (because we need to use the js optional chaining operator). Hopefully you got nvm command already installed (best way to install node), hence just do
nvm use
it's gonna use the .nvmrc file with v23.5.0
Get prepared with the env vars
cp .env-development .envnpm install
Run dev mode with
npm run dev
it's gonna start an hot dev middleware with an express server ;) ready to work http://localhost:3000
npm install
Write in your cloud provider the following environment variables BACKEND_BASE_URL & STATIC_FILES_URL
Run build mode with
npm run build
it's gonna build in dist/
Then run in production mode
npm run start
it's gonna start the only one SSR express server out of the box for internet http://localhost:3000 or environment port used.
docker build -t react-easy-ssr .
docker run -p 80:80 react-easy-ssr
Then open http://localhost:80
The main rule is we don't use a frontend framework. All components come from wherever we need them, but we are not limited to just one source. No material-ui and no bootstrap installed etc...
react-jss, style-components and fontawesome modules are installed if ever you wanna use them.
The main goal is to care of them during the SSR in order to avoid a FOUC (Flash Of Unstyled Components)
Please note we don't use classical CSS style, we use JSS (it means js in css). material-ui module also uses react-jss this is why we didn't installed material-ui else it would be stupid to generate twice the react-jss stylesheet on the SSR, and inefficiente to make an ultra fast App.
Either you install material-ui plus tailwind and you make all your css components with it (which is recommended if you do this for big company), or you get free and just install react-jss like we did.
Let's see how we manage it in our client side entry point index.tsx.
useEffect(() => {
const jssStyles = document.querySelector('#jss-server-side')
if (jssStyles?.parentNode) {
jssStyles.parentNode.removeChild(jssStyles)
}
const fontAwesomeCssStyles = document.querySelector('#fontawesome-server-side')
if (fontAwesomeCssStyles?.parentNode) {
fontAwesomeCssStyles.parentNode.removeChild(fontAwesomeCssStyles)
}
}, [])
We simply kill them from the DOM, no need of them once the server page is displayed.
Let's see how we fetch our data to feed our redux store. You can find this code in the <Movies /> component.
const { data, error, isLoading, isSuccess, isError } = useGetMoviesQuery(undefined)
Its hook is gonna be used by either the SSR and CSR, however the second one won't fetch if the first already did.
This way your App is able to fetch data on the server & client side independantly.
Let's see how it's been done in your express server.
/**
* Step 1 we are gonna execute all the React hook by doing the first renderToString.
*/
renderToString(jsx())
/**
* Step 2 you must wait as much apiSlices as you have in your configureStore.
*/
await Promise.all(store.dispatch(moviesApiSlice.util.getRunningQueriesThunk()))
/**
* Step 3 finally we are able to render all the html from React with the data inside thanks to this call to renderToPipeableStream.
*/
const { pipe, abort } = renderToPipeableStream(
<JssProvider jss={jss} registry={sheets} generateId={generateId} classNamePrefix="app-">
{sheet.collectStyles(extractor.collectChunks(jsx(helmetContext)))}
</JssProvider>,
{
onShellReady() {
...
},
onShellError(error) {
...
},
onAllReady() {
...
},
onError(error) {
...
}
}
)Webpack setup only allows us to import files with ES6 in type
- .js .jsx .ts .tsx
- .png .jpe .jpeg .gif .ico
- .woff .woff2
- .css (remember
react-jssgenerates its own stylesheet via its own plugins, not via webpack loaders)
You can add more Webpack loader to your project...
When pushing or merging on master branch, you can trigger Github Actions with a commit message that includes #major, #minor or #patch.
Example of commit message in order to start a deployment :
git commit -m "#major this is a big commit"
git commit -m "#minor this is a medium commit"
git commit -m "#patch this is a tiny commit"
- Check typescript
npm run tsc - Check eslint
npm run lint - Check prettier
npm run prettier
If ever you wanna brainstorm, download my resume you are gonna find my phone number

