Isomorphic React
Last updated
Was this helpful?
Last updated
Was this helpful?
When developing a Frontity project or package the React code (used in your custom theme) should be (also called universal).
This means that all the code in a Frontity project should be prepared to be executed both on the server-side and on the client-side.
Every time we access a page on a Frontity site the first load is rendered on the server. Once the initial server-side render (SSR) is complete the HTML is sent to the client (along with React hydration) and then the ensuing navigation is done in the client-side (CSR). ().
Let's take a look at some possible navigation examples as they would occur in an Isomorphic React App:
Scenario A (SSR)
If we enter the URL /home
in the browser's address bar and press Enter, then the page with the slug home
is rendered in the server and served to the client. If we enter the URL /contact
in the browser's address bar and press Enter, then the page with the slug contact
is rendered in the server and served to the client.
In these two cases, a SSR (Server-Side Render) process has taken the React code and created the proper HTML with the proper content that is "served" to the client so it can be displayed to the user.
Scenario B (SSR & CSR)
But consider the case where we enter the URL /home
in the browser's address bar and press Enter, and then once the page has loaded in the browser we go to the /contact
URL from a link in the page.
What then happens is:
the page with the slug home
is rendered in the server and served to the client (as before)
the page with the slug contact
is this time rendered in the client and displayed to the user
In this "Scenario B", we can call the navigation process to get to the home
page "server-side Navigation" and the navigation process to get to the contact
page "client-side Navigation"
We can also refer to these as a Server-Side Render process (SSR) in the case of the home
page, and a Client-Side Render process (CSR) in the case of the contact
page.
So, as you can see from the above image, this is something that we need to bear in mind when developing a React theme with Frontity.
Luckily, ALL the tools included with Frontity provide this isomorphic behavior out of the box (i.e. they ensure behavior that works both on the server-side and on the client-side).
The initialization (or Bootstraping) of a Frontity site happens when we do Server-side Navigation (Server-Side Render, SSR), this when we do either:
type a URL of a Frontity site in the browser's address bar and press Enter, or
reload a URL of a Frontity site (e.g. by hitting the refresh
button in the browser on a page that has been rendered in CSR)
In this process (i.e. the request of /home
in the diagram above) Frontity does the following:
Server generates the HTML for the requested page
Server sends the HTML to the client
Client hydrates the React app in top of the HTML rendered
Once these have occurred React can then take full control of the app and the navigation of the site (through clicks on links) will be handled in the client-side (i.e. the request of /contact
in the diagram above):
Client (React) requests the data from the WordPress server (if not already in the state)
Client (React) displays the proper HTML with the proper content and data
Some of these actions that you can 'hook' your code onto occur on the server-side, and some on the client-side - thus giving you the ability to specify not only when your code is executed, but also where it is executed.
On the Server Side:
Execution of the init
action
Execution of the beforeSSR
action
Server generates the HTML of the requested page
Execution of the afterSSR
action
Server sends the HTML to the client
On the Client Side:
Execution of the init
action (again)
Execution of the beforeCSR
action
Client hydrates the React app in top of the HTML rendered
Execution of the afterCSR
action
By default Frontity will create the final bundles that will be used in the server-side and in the client-side from the index.js
file in the src
folder of the theme package you're using for your Frontity project.
client.js
โ the entry point of our app when client-side takes controls
server.js
โ the entry point of our app when server-side takes controls
If Frontity finds those files, it will import the server.js
one in Node.js and the client.js
one in the browser, and it will ignore the index.js
file - which can, nevertheless, still exist.
The main use cases where you may want to use two separate client.js
and server.js
files are:
If you need to access Node.js libraries, such as "fs"
or "path"
, because they will fail if present in the client bundle ("dot-env"
for accessing environment variables, for example).
If the code contains something that cannot be exposed to the client, for example authentication details such as a hardcoded API key.
Due to the isomorphic nature of sites built with Frontity there will be some parts of the code of your Frontity project that will be executed only on the server, some parts that will be executed only on the client (i.e. the browser), and some parts that will be executed on both.
During the initialization of a Frontity app, the following parts of the code are executed only on the server
During the initialization of a Frontity app, the following parts of the code are executed only on the client-side (i.e. in the Browser).
When components are โhydratedโ, and also whenever any Client-side Navigation occurs, any hook defined in a React component (such as useEffect
) will also be executed only on the client-side.
The rest of the code inside the React components in the project will be executed either server-side or client-side depending on if we're doing Server-Side Navigation or Client-Side Navigation.
Frontity provides several actions, namely , that are executed at specific moments in the initialization process. You can 'hook' functions onto these actions to ensure that they are executed at the appropriate moments in this lifecyle.
However, you can actually create two different for your React theme package in Frontity. So, instead of having an index.js
you can have the following two files:
You can define the appropriate in either, or both, of these files (client.js
and server.js
), or in the default index.js
.
If you are using a heavy library on the server that will increase the size of the client bundle unnecessarily. For example, you can use to decode entities in the server.js
, but it . You can therefore use , which is available in the browser and so is essentially free, in the client.js
instead.
The
Functions "hooked" onto the and actions
As the is executed at Build Time, this file also has access to server-side packages and environment variables.
The
Functions "hooked" onto the and actions
This means that we can safely make use of, for example, the or the native inside of the useEffect
hook.
During the initialization of a Frontity app the function "hooked" onto the action of the is executed in both the server and the client-side.
This means that in this part of the code we should use isomorphic versions of libraries, for example using an isomorphic (as provided by the package) to fetch resources from the network as it'll work in both server and client-side.