SSR compliance #5
Closed
RaphaelEscrig
started this conversation in
OKP4 Portal
Replies: 0 comments
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
-
Github discussion
This discussion treats about the no SSR (server side rendering) compliance of the @okp4/lib. We share our library with our community so we need to be fully compliant with SSR. More and more users use SSR frameworks such as NextJS. In addition React has opened a question about the possible integration of SSR component (see here).
Before all, what is SSR (or Dynamic Rendering) ?
Server-side rendering refers to an application’s ability to display the web-page on the server rather than rendering it in the browser. When a website’s JavaScript is rendered on the website’s server, a fully rendered page is sent to the client and the client’s JavaScript bundle engages and enables the Single Page Application framework to operate.
The opposite of SSR is CSR (client side rendering). In CSR, the request redirects to a single HTML file and the server will deliver it without any content (or with a loading screen) until you fetch all the JavaScript and let the browser compile everything before rendering the content. All logic, data fetching, templating and routing are handled on the client rather than the server.
SSR is a good option if you want:
SSR can be costly and ressource intensive (each page will be generated from the server), more complex to implement, limited with third party JavaScript code (our case with our lib) or can have slow load times for a complex application.
Why do we have SSR troubles ?
For our web applications we use Next.js framework. Next.js optimize the render of each page. To optimize it, we can choose the render method Next will use. We have the choice with all theses methods: SSG, CSR, SSR, ISG. Here is the documentation about it: click me.
Here is a link with an explication of each possible render methods: click me.
Basically, with SSG / CSR, all html pages will be generated on build time. With SSR, it will be generated on each request. By default Next renders the page in SSG. If we want to inform Next to generate the page with SSR, we need to export a function named:
getServerSideProps
.Today we haven’t (yet) chosen SSR as render method. So why Next.js raises SSR errors with our library if we don’t use SSR as render method ? It’s because Next.js pre-renders every page (for performance issue). This pre-renders is done on server side. This means that Next.js generates HTML for each page in advance, instead of having it all done by client-side JavaScript. Each generated HTML is associated with minimal JavaScript code necessary for that page. When a page is loaded by the browser, its JavaScript code runs and makes the page fully interactive. This process is called hydration).
Today because the lib is not fully compliant with SSR, if Next pre-renders a page using the lib, an error will be raised.
The main raised error is this one:
Document is not defined error raised with SSR issue
We have this error because our lib uses the window api of the navigator (see the list here) and when Next pre-renders the page (on server side), it can’t access to the window. Our library is also using localstorage which is not available on server side.
What do we need to change ?
We’re not compliant with SSR on several parts of the UI. Many components use the classnames package. This package uses the window so we can’t use it anymore. At the moment, it’s the only no-compliant package I found on the lib. Our configuration of the i18next package is working only on client side; we store the saved language in the localstorage. The saved theme (inside the ThemeProvider) is also saved in localstorage. Finally, many hooks are using window or localstorage navigator APIs.
Here is a resume of what I have isolated, tested and patched:
Atoms
Molecules
External Packages
Hooks
How can we fix it ?
Externals packages
For the non compliant SSR packages (only classnames at the moment), we must ensure to not use them anymore. For classnames, we need to found another package which does the same thing or we need to change the way we create dynamic classnames. Material UI has created its own system (with Material StyleSheet) and radix uses pure react / css features.
Browser APIs
With our hooks using navigators APIs, we have two strategies. The first one is to add guards dom. A guard dom will check if we are on client side or not. We can detect it by a simple check on the window.
Example of a guard dom
If we decide to use this solution, we must think about the default return on server side. For example, in the hook “useMediaType” we always return a boolean. On server side, the return will always be false because we can’t check the
window.matchMedia
. We will need to inform the user about this behaviour on SSR.The second strategy is to replace all our browser APIs calls. Instead of using the localstorage, we could use some cookies. Cookies are working on client and server side but they need to be accepted by the user. It’s more intrusive than localstorage or sessionstorage.
If we take a look to Material UI / Radix, we see that they store the saved theme inside the localstorage. If nothing is found on localstorage, they use a default theme (light).
Another way to get rid of the window is to use ref. Indeed, in certains circonstances, ref can replace window calls. A ref is a way to access to DOM nodes or React elements. You can read more about it here. With ref, we can use the ref forwarding technique. As the document said, this technique is useful in component libraries. If you check the Material UI code, you’ll see they use it at many times. Basically ref forwarding is a technique to send the parent ref directly to one of its children.
Material UI and Radix can guide us in our choice. They both use SSR as render method (see the network response of their homepage). Because it’s SSR, if you deactivate Javascript or not on your browser, the page will still be perfectly loaded and displayed. If you activate JS and you chose to display the website on dark mode (by clicking on the adequate button of each website), you’ll see the page in dark. Now go to the network and analyse the preview of the response. The returned html is always in light mode. We can conclude it’s the client who decides which theme to display. If the client doesn’t find a light or dark theme, it will render the response html.
Response of the server when dark mode is activated on mui.com
Response of the server when dark mode is activated on radix-ui.com
If we dig into the material ui packages code, we can see the usage of a guard dom with a default return if we are on server side. You can check the code here. In the
useMediaQuery
function, they detect (with a guard dom)supportMatchMedia
the render method (CSR / SSR). Once detected, they store it inside a variable:supportMatchMedia
. They send this variable to theuseMediaQueryNew
function. They return a default value is we are on SSR:Sample of code of the useMediaQueryNew function
For i18next, we can use the same logic: by default, we use the english language and if the client find another language inside the localstorage, we use it. I18Next can be run on server with the Backend plugin.
In addition,
useEffect()
function will not be run on SSR so we can move our CSR logic insideuseEffect()
too.Conclusion
Today our lib is not compliant with SSR because of multiples reasons. We need to discuss which strategy we want to implement. Do we want to have a library working perfectly on server side or as Material ui / Radix do we want to just be compliant with SSR ? I think we need to decide it before our refactor of the lib (new features and components design). We also need to consider the ressources we have to develop / maintain our library. It’s mainly theme switch / language switcher and some hooks who are not compliant. Today we only have a few components but planned to develop more components in a near future. The more we think now on how to be compliant with SSR, the fastest the future developments will be.
I think we should also mention (inside our Readme) that at the moment, we are not compliant with SSR.# Github discussion# Github discussion
Beta Was this translation helpful? Give feedback.
All reactions