Recently I developed a Reactjs project base on the SSR technique, I’ve learned a lot. Now I want to consider some of the blind points in Reactjs SSR.

Maybe you’ve already known. But I think it would be useful to the developers who want to try SSR technique in Reactjs projects.

At first point, you need to know about what difference between client-side rendering and server-side rendering.

CSR vs. SSR

On regular SPA applications, we import the JS bundle files to the empty HTML file as script tags.

We have these steps on initial page load.

The user types the web page URL and press enter. After some DNS manner, the GET request reaches to the server The server sends an HTML file as response. The browser is parsing the HTML and sees oh some external JS scripts should be downloaded. After waiting to download JS files. The browser parses and runs them. The root component is mounted it, and the componentDidMount event would be triggered, an async request run to get the data from the API. The data get from the server, and that component will be re-rendered to show the new data.

As you can see the data fetching and binding handle on the client side, We called it CSR ( Client Side Rendering ).

On the other hands, we have SSR ( Server-Side Rendering ) with these steps. For better understanding, You can see this sequence diagram.

Server-Side Rendering ( SSR )

As you can see the user would be able to see the whole web page before the JS bundle files are downloaded.

We can use defer attribute for JS scripts since we have a completed HTML file with data and we don’t need to execute scripts as soon as possible.

// You can find this line on server.js - razzle base project

<script src="${assets.client.js}" defer></script>

Which boilerplate should I use?

You could pick either next.js or react-server or Razzle. At first, I recommend seeing their documents. I found Razzle much simpler.

Razzle is a tool to abstract all of the complex configurations that needed for SSR into a single dependency. You are not limited to Reactjs; you also can use it with Reason, Elm, Vue, Angular as well.

At first, you start the project with zero configuration. Later you would be able to customize all of the settings according to your needs. You can even extend the Webpack configs easily.

How can we reach to initial method on the server side?

In Reactjs we are thinking in components. On CSR we always call the initial method on the componentDidMount event. So we need to access the initial method on server-side base on the route.

It’s a little complicated. Let ‘s see in action.

We usually define routes base on path and components like this. We could use the Route component everywhere.

const MainApp = () => (

<Switch>

<Route exact path="/" component={App} />

<Route path="/login" component={Authorize} />

</Switch>

);

There is a problem on SSR since we want to access to the component base on URL on the server. So we need to define all of the routes in one place in this way.

const routes = [

{

path: "/",

component: App,

initialMethod: () => getSomeData(),

routes: [

{

path: "/",

component: Dashboard,

exact: true,

initialMethod: () => getDashboardData(),

},

{

path: "/notifications",

component: Notifications,

beAuthorized: true,

initialMethod: () => getLatestNotifications(),

},

{

path: "/settings",

component: Settings,

beAuthorized: true

routes: [

{

path: "/settings/profile",

component: ProfileSettings,

initialMethod: () => getUserProfile(),

}

],

}

]

},

// other root routes like /login

];

We could define the initialMethod here or inside the Component as a method. You should note this method must be a Promise .

Now we should render routes base on Route component. At first, we need a Custom Route component to handle custom routes and beAuthorized attributes.

import React from "react";

import { Route, Redirect } from "react-router-dom"; const CustomRoute = ({

component: Component,

cmpProps = {},

routes,

exact = false,

path,

beAuthorized = false

}: Props) => {

// detect is user logged in or not

const isUserLoggedIn = getState().user;

// redirect the guest user on the protected path

if (beAuthorized && !isUserLoggedIn) {

return <Redirect to="/login" />;

}

return (

<Route

path={path}

exact={exact}

render={props =>

<Component

{...props}

{...cmpProps}

routes={routes}

/> }

/>

);

};

We passed routes attribute to the Component. For example, on Settings components, we could reach to routes via routes props.

We also define a component to iterate through the routes.

import React from "react";

import { Switch } from "react-router-dom";

import CustomRoute from "../CustomRoute"; const Routes = ({ routes, switchProps = {} }: Props) => (

<Switch {...switchProps}>

{routes.map((r, i) => (

<CustomRoute

key={r.key || i}

path={r.path}

component={r.component}

exact={r.exact}

beAuthorized={r.beAuthorized}

routes={r.routes}

/>

))}

</Switch>

);

On the server we could get the initialMethod base on match route. We have a helper function called matchPath, for more information see this document.

import { matchRoutes } from 'react-router-config' // inside a request /../

// router.get("/*",(res, req){ /.../ }) const branch = matchRoutes(routes, location.pathname) const promises = branch.map(({ route, match }) => {

return route.initialMethod

? route.initialMethod(match)

: Promise.resolve(null)

}) Promise.all(promises).then(data => {

// now you can send the rendered view to user

})

We are using Promise.all() to wait until all of the initialMethod has been resolved.

For example, if the request URL is / the Dashboard component should be rendered. In this case, we the two initialMethod to perform as Promise, getSomeData() and getDashboardData() . Could be two async API request, so you should notice to the performance issues.

The best way to test server-side fetching is disabling the JavaScript, how we can do that?

1- Go to Chrome Developer Tools

2- Go to Settings ( Press F1 )

3- Check the Disable JavaScript from Debugger section

You can test it on the Producthunt website.