best practice for conditional rendering with SSR in Next.js - next.js

in my Next.js app i want to render a mobile-/ desktop-header based on the width of the device. In general this is a simple task, i did many times in normal react, using hooks like useWindowDimensions, where I check the width of the window object and use this parameter for a simple condition.
In Next.js however I ran into the problem, that my app is pre-rendert on the server, so of course there is no window object, that I could use for the condition.
Things I tried:
dynamic import of the component, this means the component didn't get pre-rendert on the server, but only on the client. This would work, but I didn't use the benefit of SSR, and for SEO reasons I want to pre-render "key-components" like the header.
I just picked a condition for SSR like render always the mobile-header for example and on the CSR I just render on the real condition. This would solve my issue with render always "key-components" on server side, so SEO is happy, but i run in an ugly warning about content mismatch between server and client, because when I render the app on desktop device, my first render on client side of course would be the desktop-header. So this doesn't seem to be a good solution either.
Next I tried to render a certain condition like before, so render always the mobile-header on server AND client side and use a useEffect hook only on mount useEffect({...}, []) which then check the real condition and trigger a re-render with the correct condition. This would solve my SEO-thing, and also the ugly content mismatch warning. BUT I run into a noticeable layout-shift, where the user sees the mobile-header first and after half a second the header changes to the desktop-header. ugly stuff..
So I got my next idea, which was checking the user-agent or device type with getServersideProps and somehow use this info for a conditional rendering on server-side. This doesn't really work out to well, specially I would want to use it in my _app.tsx so I don't have to write this stuff for every page again and again. In a discussion thread in the official Next.js repo I found, that getServersideProps doesn't work in _app.js yet, only getInitialProps, which is deprecated, and shouldn't be used anymore...
So, to make things short, there has to be a good way to render components in Next.js without the above problems. I just couldn't figure out yet, what is best practice for this kind of stuff.
I would appreciate any hints, tipps or advices in this topic ❤️

Related

On the discouragement of getInitialProps

I'm currently implementing a POC project in NextJs 12 to check whether it is possible for us to integrate it. I'm not able to update to 13 yet since I cannot support React 18 due to some in-house packages that we use, so for the purposes of this discussion let's pretend that Next 13 does not exist yet.
We all know that Next officially discourages the use of getInitialProps and recommends using getServerSideProps wherever possible. I'm aware of all the downsides of getInitialProps as opposed to getServerSideProps, main one being the constant client/server context switch which you have to be mindful of.
However, I cannot understand the easiness with which this is discouraged. Apart from NextJs itself I've seen a lot of blog posts calling it the worst thing ever and such. It seems to me like people who say stuff like that have not had some realistic use cases, and that the opinion mostly comes from toy projects (notes app, todo app, blog app etc).
Anyway the purpose of this question is twofold. One, to verify if it is at all possible in my case to avoid getInitialProps, and two, to see if anyone else thinks that this discouragement is somewhat unfounded and not based on reality.
The reason I've decided to use Next at all was to achieve SSR in React. The entire point of that, at least I believe so, is to enable SSR and still preserve some main benefits of React, such as a seamless SPA-like navigation in certain pages. If that was not the case I would have gone for a traditional SSR framework such as Ruby on Rails, Django, etc.
The reason why I need to use getInitialProps, and why I believe I cannot possibly avoid it, is based on two aspects:
Every single page that I have requires certain global data, which I don't want to refetch on every route.
The perfect example of this is the page header. The header of every one of my page depends on user data and translations (i18n). Both of these things I fetch from an external API. If I were to use GSSP then every route and every sub-route of every page would have to re-perform this data fetching which seems like a huge performance kill. I have no way to properly persist this data through GSSP navigation without conforming to some hacks such as sending hidden query parameters which check if data was already fetched. If we were to assume that the user always comes into contact with a page solely through its root URL, then this would work, but this assumption is extremely unrealistic.
By using getInitialProps in combination with redux and next-redux-wrapper, it is very easy to check if data was already fetched (even better if using RTKQ you don't even have to explicitly check it).
Big pages where I want SPA-like behaviour are not possible.
In my case I have one page which has about 5 sub-routes. On its homepage, we display a list of certain entities, which is fetched from the API. The API is such that entities can only be fetched as a list, and you cannot fetch entities individually. Then, for each entity, when you click on it, you can go to a sub-page where you see its specific info.
The only natural way to do this is to have the entire data fetched on the first page visit and then reused throughout the page as we navigate. Re-fetching the whole data on every page navigation is also a performance kill. The only way I was able to implement this and preserve a seamless SPA-like navigation was with getInitialProps.
What's interesting about this use case is that hacks with sending hidden query params would actually not do the trick, since even though I can force GSSP to be aware that the data is already fetched, I cannot access that data, therefore I cannot do any server-side route validation. What I mean is that if a user was to land on the home page, where all the entities are fetched, then somehow visit an entity page, like page/123, where an entity with id 123 does not exist, I cannot validate that and properly handle it in GSSP without re-fetching the entire list of entities again which is, once more, a performance kill.
So, in conclusion, I'd like to hear opinions on the discouragement of getInitialProps. For me it seems borderline impossible to completely migrate to getSeverSideProps if your app is at all realistic, uses translations, global data, etc.
Thanks in advance.

Does MUI require all pages to be CSR when using the new app directory in NextJS 13

I just started my first NextJS app and followed this guide to add MUI. The only difference I can see is that I'm using the experimental app directory of NextJS 13.
When following the guide, I get a server error of Uncaught TypeError: React.createContext is not a function which is basically rather clear, as the context API is only available to client components and can therefore rather easily be "solved" by placing the 'use client'; directory on top of the erroneous component.
Unfortunately it seems like this is required for every component which uses any part of MUI. E.g. I'd also have to 'use client;' in my root layout.tsx if I'd like to wrap my whole app with the ThemeProvider etc.
My questions are the following:
Do I really have to 'use client;' everywhere I'd like to use MUI or is there some other way around?
If 1 is true: Doesn't this make MUI completely useless in combination with NextJS as it prevents the usage of all the SSR, SSG, ISR goodness etc or do I have some kind of misunderstanding here (I'm very new to NextJS in general...)?
If 1 & 2 are true: Are there other recommendable UI frameworks or libraries which bring a good set of "ready to use" (and out of the box good looking) controls, components etc. that work better with NextJS without those limitations?
FYI, I have the following versions installed ATM:
next#13.0.6
#mui/material#5.10.17
Update
I should have read the NextJS beta docs more careful before posting the question:
It seams like there's no way around this ATM as stated here in the docs and again here in the a ticket.
So this answers question 1, as there doesn't seem to be a way around 'use client'; for now.
However, I'd still really appreciate answers and opinions on questions 2 & 3.

Displaying multiple feature modules that use router in the same view in Angular

I have an use case in which I need to have multiple modules loaded in the view at the same time. Thing is, each of those modules might be as simple as a component, or a complex Angular module with a router and everything. I guess you could call it a plugable framework.
The number of modules I have to show or who they are is dynamic (I'm getting them from a server).
My first idea is that it would be good for this case if the feature module's router wouldn't be a singleton with the main one and also if they wouldn't update the url completly.
Each of the feature module should be able to be launched as a stand-alone app if bootstrapped (therefore, I do need it to be able incorporate all Angular 4 features including the router).
I managed to get something working by playing with the router and with named router outlets (secondary routes) but not sure how good that is in the long run.
How should I approach this?
I am in the early stages of attacking a somewhat similar scenario. In my app, the UI is organized in panels that are designed to stand alone on smaller displays, or side by side on larger. I find this approach works well for my routes, as my app is designed to edit a complex, highly hiegraphical document with many inter-related sections. e.g. User follows link on list to open detail, new detail panel appears to right... follows link on detail to related node... etc.
The solution I'm noodling with now is attempting to use an ngrx store that interacts with the router (and router-store) to dynamically create panels based upon router data. Well, technically it does not create the panels, it only serves the data that tells a component what to create and render.
I know my situ is not exactly the same as yours, but perhaps there's something in my approach that may help you find usefull or at least thought provoking.

Implementing Timeout Javascript in a PeopleSoft iScript

Regular PeopleSoft components automatically generate a timeout popup window when a user is about to be logged out. iScripts, however, do not.
I was about to reverse-engineer the javascript that PeopleSoft automatically generates for components in order to leverage existing code.
Before I do, I thought I'd check if anyone else has done this, and if there are any gotchas, tips or tricks. Or maybe there's a better way?
I reverse engineered how the Navigation Collections page (which is an iScript) works since it also handles timeouts. The solution is relatively simple.
First, include the results of %Request.ExpireMeta (PeopleCode function) in the portion of the HTML you generate.
Secondly, include the delivered javascript PT_SAVEWARNINGSCRIPT in the HTML you generate.
Finally, have your iScript call the javascript function setupTimeout().

Rendering javascript at the server side level. A good or bad idea?

Now a community wiki!
I want to make it clear first: This isn't a question in relation to server-side Javascript or running Javascript server side. This is a question regarding rendering of Javascript code (which will be executed on the client-side) from server-side code.
Having said that, take a look at below ASP.net code for example:
hlRemoveCategory.Attributes.Add("onclick", "return confirm('Are you sure you want to delete this?');")
This is prescribing the client-side onclick event on the server-side.
As oppose to writing Javascript on the client-side:
$('a[rel=remove]').bind('click', function(event) {
return confirm('Are you sure you want to delete this?');
}
Now the question I want to ask is: What is the benefit of rendering javascript from the server-side code? Or the vice-versa?
I personally prefer the second way of hooking up client-side UI/behaviour to HTML elements for the following reasons:
Server-side does what ever it needs to already, including data-validation, event delegation and etc; and
What server-side sees as an event is not necessarily the same process on the client-side. i.e., there are plenty more events on client-side (just look at custom events); and
What happens on client-side and on server-side, during an event, could be completely irrelevant and decoupled; and
What ever happens on client-side happens on client-side, there is no need for the server to know. Server should process and run what is given to them, how the process comes to life is not really up to them to decide in the event of the client-side events; and so on and so forth.
These are my thoughts obviously. I want to know what others think and if there has been any discussions on this topic.
Topics branching from this argument can reach:
Code management: is it easier to render everything from server-side?
Separation of concern: is it easier if client-side logic is separated to server-side logic?
Efficiency: which is more efficient both in terms of coding and running?
At the end of the day, I am trying to move my team to go towards the second approach. There are lot of old guys in this team who are afraid of this change. I just wish to convince them with the right facts and stats.
Let me know your thoughts.
UPDATE1: It looks like all of us who have participated in this post have common thought; Good to know that there are others who think alike. Now to go convince the guys ;) Thanks everyone.
Your second example is vastly superior to the first example. Javascript is your behaviour layer and should be separate from your semantic markup (content) and CSS (presentation). There are a number of reasons this is better architecture:
Encourages progressive enhancement. As you mentioned, the backend code should work correctly in the absence of JS. You cannot rely on your clients having JS available. This way you build it once without JS and then can enhance the experience for those with JS (e.g. by adding clientside validation as well as serverside validation so that the client can get instant feedback)
Cleaner markup. Normally reduced download size. One reusable selector in a separate JS file that can be cached and shared between pages vs. a handler on each element.
All of your JS in one re-used place. e.g. if your code was opening a popup window and you decided to change the dimensions of the window you would change it once in the code in the JS file vs. having to change it on every individual inline handler.
There are lots of other arguments and reasons but they should get you started...
Also, from your example it appears that you have a normal link in your document which can delete content. This would also be a bad practice. Anything that deletes or updates content should be done on a POST (not GET) request. So it should be the result of submitting a form. Otherwise e.g. googlebot could accidentally delete all of your content by just crawling your page (and search engine robots don't execute JS so your alert wouldn't help there)
The two biggest differences i can think of up front are:
you lose the client side caching you would get if the javascript was in a separate js file
if you need to change your javascript, you have to recompile (extrapolate this to what happens after you have released your product: if you have to recompile then you need to redistribute binaries instead of just a modified js file)
it is easier to use the VS debugger if the javascript is in a separate file; you can just set a break point in that file, if you are generating the code server side then you have to use the running documents feature, find your generated code and then add the breakpoint, and that breakpoint has to be manually added everytime you re-run your app. Following on from that, if the code is in a separate file, then you can just make your tweak to the javascript code, F5 your browser page, and keep on debugging without having to stop and restart the debugger.
It should be mentioned that sometimes you have to insert js code from the server - for example if the bulk of your code is in a separate js file and you need to insert control identities in to the page for that code to work with. Just try to avoid that situation if possible.
Looks like you already know what to do. Rendering it on the server side is a bad idea.
The simple reasoning being you're Javascript lives both on the server side pages as well as in separate Javascript files (assuming you are using Javascript at all). It can become a debugging nightmare to fix things when everything is everywhere.
Were you not using any other Javascript besides what the server side scripts generate, it would probably be fine and manageable (forget what the unobtrusive movement says).
Secondly, if you have 100 links on the page, you will be repeating that same code in 100 places. Repetition is another maintenance and debugging nightmare. You can handle all links on all pages with one event handler and one attribute. That doesn't even need a second thought.
<Rant>
It's not easy to separate HTML and Javascript, and even CSS especially if you want some AJAX or UI goodness. To have total separation we would have to move to a desktop application model where all the front-end code is generated on the client side programmatically using Javascript, and all interaction with the server gets limited to pure data exchange.
Most upstream communication (client to server) is already just data exchange, but not the downstream communications. Many server-side scripts generate HTML, merge it with data and spit it back. That is fine as long as the server stays in command of generating the HTML views. But when fancy Javascript comes onboard and starts appending rows to tables, and div's for comments by replicating the existing HTML structure exactly, then we have created two points at which the markup gets generated.
$(".comments").append($("<div>", {
"id": "123",
"class": "comment",
"html": "I would argue this is still bad practice..."
}));
Maybe this is not as big a nightmare (depending on the scale), but it can be a serious problem too. Now if we change the structure of the comments, the change needs to be done at two places - the server side script and templates where content is initially generated, and the Javascript side which dynamically adds comments after page load.
A second example is about applications that use drag and drag. If you can drag div's around the page, they would need to be taken off the regular page flow, and positioned absolutely or relatively with precise coordinates. Now since we cannot create classes beforehand for all possible coordinates (and that would be stupid to attempt), we basically inject styles directly in the element. Our HTML then looks like:
<div style="position: absolute; top: 100px; left: 250px;">..</div>
We have screwed up our beautiful semantic pages, but it had to be done.
</Rant>
Semantic, and behavioral separation aside, I would say is basically boils down to repetition. Are you repeating the code unnecessarily. Are multiple layers handling the same logic. Is it possible to shove all of it into a single layer, or cut down on all repetition.
You and the other people answering the question have already listed reasons why it is better not to having the server side code spit intrinsic event attributes into documents.
The flip side of the coin is that doing so is quick and simple (at least in the short term).
IMO, this doesn't come close to outweighing the cons of the approach, but it is a reason.
For the code in your example it doesn't really matter. The code isn't using any information that is only available at the server side, so it's just as easy to bind the event in client side code.
Sometimes you want to use some information that is available at the server side to decide whether the event should be added or not, or to create the code for the event, for example:
if (categoryCanBeDeleted) {
hlRemoveCategory.Attributes.Add(
"onclick",
"return confirm('Are you sure you want to delete the " + categoryType + "?');"
);
}
If you would do this at the client side, you have to put this information into the page somehow so that the client side code also has access to it.

Resources