How to entirely disable server-side rendering in next.js v13? - next.js

The documentation says that I can disable server-side rendering by adding 'use client' to the top of files that need to be rendered only client-side.
However, in practice, I've added this header to every file in the project, and I see that both Layout and Page are being rendered server-side.
I can confirm this with a simple page:
'use client';
export default () => {
console.log('SERVER RENDER (page)');
return (
<div>test</div>
);
};
I would expect next dev to not output "SERVER RENDER (page)", but it does.

You don't need to disable server-side rendering (SSR), as it's not enabled by default in Next.js.
It pre-renders every page unless instructed otherwise, using either Static Generation or SSR.
Static Generation. The HTML generated at build time and will be reused at every request.
SSR. The HTML is generated on each request.
Next.js uses Static Generation whenever possible.
In your example, SSR doesn't happen. You're seeing the SERVER RENDER (page) message in terminal because you run it in dev (next dev) mode. In dev mode there is no build done, so the pages are generated on the go. You won't see this message in production mode. See this for more details.
Using use client; directive doesn't change the above, it just tells Next.js that a component is a Client Component, which will still be pre-rendered.

It looks like even if a component is marked 'use client', it will still be pre-rendered.
Client Components enable you to add client-side interactivity to your application. In Next.js, they are prerendered on the server and hydrated on the client. You can think of Client Components as how Next.js 12 and previous versions worked (i.e. the pages/ directory).
https://beta.nextjs.org/docs/rendering/server-and-client-components#client-components
#Nikolai pointed this out correctly, but did not answer how to disable SSR.
However, now that we know that Next 13 behaves the same as 12, we can also apply the same hydration workaround that was used in previous versions.
The TLDR is that you want to wrap your layout in a component that conditionally renders the element based on whether it detects the browser environment, e.g.
const Dynamic = ({ children }: { children: React.ReactNode }) => {
const [hasMounted, setHasMounted] = useState(false);
useEffect(() => {
setHasMounted(true);
}, []);
if (!hasMounted) {
return null;
}
return <>{children}</>;
};
export default ({ children }: { children: React.ReactNode }) => {
return (
<html lang="en">
<head />
<body>
<Dynamic>{children}</Dynamic>
</body>
</html>
);
};
Obviously, make sure you know what you are doing. This is generally not desired behavior, though there are exceptions.

Related

Hybrid render with Next.js?

I'm new to Next.js and I'm using it to perform server side rendering on the landing page.
The landing page has: 1 generic component that's the same to every user and 1 component that is specific for each user.
Is it possible to perform server side rendering on the generic component, and client side rendering on the specific one?
Thank you.
Yes, you can do client-rendering for any component in your hierarchy. Client rendering usually means that when the component first renders, it fires off some asynchronous request for data (from an API, etc).
In your SSR page, just have your user-specific component not render anything on the initial render (except maybe some loading UI). Then include a useEffect hook that triggers the API call and sets state (local or global state as appropriate) which will trigger your component to re-render with the user-specific data.
During SSR, only the loading state will render. As soon as the component is mounted, the useEffect will trigger and the user-specific data will load and the component will re-render.
Overly simplistic example:
const UserGreeting = () => {
const [name, setName] = setState();
useEffect(() => {
getUserNameAsync().then((data) => {
setName(data.name);
})
}, [setName])
if (!name) {
return <div>...</div>
}
return (
<div>Welcome, {name}</div>
)
}
To make a page both dynamic and static at the same time is possible.
the solution for dynamic: you have to use react useState then useEffect to send the request after unloading fishing on the client side
but first must use next.js api getStaticProps() make the page static user's first visit

Next.js advanced client-side routing

In a Next.js app (full-featured, not next export) that uses React Context for state management and the file-system based router, how can you implement advanced routing?
I want to have preconditions for certain pages, so for instance if you try to load /foo but the Context doesn't have a given property set correctly, it'll route you to /bar.
The actual logic is complex and varies by page, so I'm looking for an approach that's easy to maintain.
Note that these preconditions are not authorization-related, so they do not need to be enforced server-side. It's more like "you need to fill out this form before you can go here."
The use of Context imposes some constraints:
Context must be accessed in a React component or in a custom Hook
Using a custom server for routing is not an option, as that would lose the Context - it has to use client-side routing
The current Context has to be checked (I tried decorating useRouter, but if the Context was changed right before router.push, the custom Hook saw the old values)
Update: It's also good to avoid a flash when the page loads before rerouting happens, so a side goal is to return a loading indicator component in that case.
I believe you can create a HOC and wrapped every pages with you HOC that takes arguments e.g. { redirects: '/foo' }
// pages/bar.tsx
const Page = () => {...}
export default RouteHOC({ redirects: '/foo' })(Page)
the HOC file will be something like this
// hoc/RouteHOC.tsx
const RouteHOC = ({ redirects }) => (WrappedComponent) => {
// you can do your logic here with the context.. even filling up a form here
// too also can.. (like returning a modal first before the real Component).
// useEffect work here too..
const { replace } = useRouter()
// then after you want to replace the url with other page
replace(redirects)
return WrappedComponent
}
This is pretty okay to be maintainable I think. You just create all the logic in HOC and when you want to update the logic - you just have to edit it in 1 file.
Well this is one option I can think of when reading your question - sorry if I misunderstood it in any way. There will always be a better way out there as we all know we can improve and adapt to new situation every seconds :D. Cheers 🥂!!
You can do this.
const Component = () => {
const example = useExample()
return <div id='routes'>
<a href='/example1'>Example 1</a>
{example.whatever && <a href='/example2'>Example 1</a>}
</div>
}

Using `within` in custom helpers

I'm using CodeceptJS and I'm trying to write a custom helper that asserts an text and clicks "OK". This dialog pops up as a iframe modal to consent with cookies.
If I write following steps in my scenario
I.amOnPage('/some-path');
within({frame: '#iframeID'}, () => {
I.see('Headline text for dialog');
I.click('OK');
});
// ...
...my test seems to work just fine.
But when I make an custom helper out of that and configure it properly so I can use it:
const { Helper } = codeceptjs;
class CookieConsent extends Helper {
consentWithCookies() {
const { Puppeteer } = this.helpers;
within({frame: '#iframeID'}, () => {
Puppeteer.see('Headline text for dialog');
Puppeteer.click('OK');
});
}
}
module.exports = CookieConsent;
...and use it as a step:
I.amOnPage('/some-path');
I.consentWithCookies();
// ...
...it doesn't seem to work as the consent dialog doesn't get clicked away as it was when implementing this directly in the scenario. According to some console.log() debugging the within callback doesn't get called at all. Console doesn't throw any errors about undefined within or anything suspicious.
I suspect that using within in a custom helper isn't working or I'm doing something wrong that I can't figure out from the documentation.
This warning at documentation doesn't really clarify when within is being used incorrectly, and using await doesn't help the problem.
within can cause problems when used incorrectly. If you see a weird behavior of a test try to refactor it to not use within. It is recommended to keep within for simplest cases when possible. Since within returns a Promise, it may be necessary to await the result even when you're not intending to use the return value.
iFrames can be a pain to work without when it comes down to automation. There are a number of factors that can make an iFrame unreachable to a framework such as cross-domain iFrames, commonly used for increased security on the content served.
Now to fix your issue, all you have to do is use switchTo() - Docs in CodeceptJS which is a function available for all helpers made available. The order should be
I.switchTo('your iframe');
..... some actions here;
I.switchTo(); // You do this so that you get out of the iFrame context when done

NextJS serverside Breakpoints possible?

Is it possible to set breakpoints in NextJS serverside code? I've got a debugger in my getInitialProps and it never breaks at that point. It only breaks when it's ran on the browser, server side breakpoints never seem to catch.
What a beautiful question you asked!
getInitialProps It call when the component is called. Just like componentDidMount in react. The difference is that...
You must send the props it needs before calling.
Otherwise ssr will not work.
This method does not display the console and you must return and display the parameter in the method
static async getInitialProps = () => {
return {custom: 'value'}
}
render() {
return(
<div>
{JSON.stringify(custom)}
</div>
);
}
Just in case this question has not been answered:
getInitialProps in nextjs is a server-side function. So, in VSCode, you will have to start the server (npm run dev) in debug mode in order to "hit" the breakpoints in the server-side code. Likewise, you may launch the client side (browser) in debug mode in order to "hit" the breakpoints in the client-side code.

Why is mixing Razor Pages and VueJs a bad thing?

I'm trying to set up a .NET core project using Razor Pages and include vueJs inside the razor page for all my logic.
Something like this:
#{
ViewData["Title"] = "VueJs With Razor";
}
<h2>#ViewData["Title"].</h2>
<div id="app">
<span>{{ message }}</span>
</div>
<script>
new Vue({
el: '#app',
data: {
message : 'Hello vue.js'
}
})
</script>
I have read that mixing Vue and Razor pages is a bad practice, and one should use Razor OR Vue.
Why is this?
Mixing VueJs and Razor Pages is not necessarily a bad thing, it can be great!
I use Vue with razor for non SPA pages and the two work well together. I choose to use Vue by loading it via a script tag from a CDN and and I do not leverage the use of WebPack for transpiling, I simply write my code in (gasp) ES5. I chose this approach for the following reasons.
Using Razor pages rather than a SPA aids in SEO and search engine ranking of public facing pages.
Loading Vue directly from a CDN eliminates a whole stack of Webpack centric technology from the learning curve which makes it much easier for new devs to get up to speed on the system.
The approach still provides the reactive goodness to UI development that Vue inherently brings to the table.
By keeping with the “page model” the code that delivers site functionality is logically grouped around the backend page that delivers that functionality.
Since Vue and Razor can do many of the same things, my goal for public facing pages is to use Razor to generate as close to the final html as possible, and to use Vue to add the reactiveness to the page. This delivers great SEO benefits for crawlers that index the page by parsing the HTML returned.
I realize the my usage of Vue is quite different than going the route of a SPA and WebPack and the approach often means I can't use 3rd party Vue Components without reworking the code a bit. But the approach simplifies the software architecture and delivers a lightweight reactive UI.
By using this approach Razor can be heavily leveraged to generate the initial rendering of the HTML with some tags containing vue attributes. Then after the page loads in the browser, Vue takes over and can reconfigure that page any way desired.
Obviously, this approach will not fit the needs of all developers or projects but for some use cases it's quite a nice setup.
A few more details for those interested
Since I use vue sitewide, my global _layout.aspx file is responsible for instantiating vue. Any sitewide functionality implemented in vue is implemented at this level. Many pages have page specific vue functionality, this is implemented as a mixin on that page or a mixin in a js file loaded by that page. When the _layout.aspx page instantiates Vue it does so with all the mixins that I have registered to a global mixin array. (The page pushed it's mixin on that global mixin array)
I don’t use .vue files. Any needed components are implemented either directly on the page or if they need to be used by multiple pages then they are implemented in a partial view like the one below.:
dlogViewComponent.cshtml :
#* dlog vue component template*#
<script type="text/x-template" id="dlogTemplate">
<div class="dlog" v-show="dlog.visible" v-on:click="dlog.closeBoxVisible ? close() : ''">
<div class="dlogCell">
<div class="dlogFrame" ##click.stop="" style="max-width:400px">
<i class="icon icon-close-thin-custom dlogCloseIcon" v-if="dlog.closeBoxVisible" ##click="close()"></i>
<div class="dlogCloseIconSpace" v-if="dlog.closeBoxVisible"></div>
<div class="dlogInner">
<div class="dlogTitle" style="float:left" v-text="title"></div>
<div class="clear"></div>
<div class="dlogContent">
<slot></slot>
</div>
</div>
</div>
</div>
</div>
</script>
#* Vue dlog component *#
<script type="text/javascript">
Vue.component('dlog', {
template: '#dlogTemplate',
props: { //don't mutate these!
closeBoxVisible: true,
title: 'One'
},
data: function () {
return {
dlog: { //nest the data props below dlog so I can use same names as cooresponding prop
closeBoxVisible: (typeof this.closeBoxVisible === 'undefined') ? true : (this.closeBoxVisible == 'true'),
title: (typeof this.title === 'undefined') ? '' : this.title,
visible: false
}
}
},
methods: {
//opens the dialog
open: function () {
app.hideBusy(); //just in case, no harm if not busy
this.dlog.visible = true;
var identifyingClass = this.getIdentifyingClass();
Vue.nextTick(function () {
$("." + identifyingClass).addClass("animateIn");
fx.manageDlogOnly();
});
},
//closes the dialog
close: function () {
fx.prepDlogClose();
var identifyingClass = this.getIdentifyingClass();
this.dlog.visible = false;
$("." + identifyingClass).removeClass("animateIn");
},
getIdentifyingClass: function () {
if (this.$el.classList.length > 1) {
//the last class is always our identifying css class.
return this.$el.classList[this.$el.classList.length - 1];
} else {
throw "A dialog must have an identifying class assigned to it.";
}
}
}
});
</script>
In the above, it's the Vue.component('dlog', ... part of the js that installs the component and makes it available to the page.
The vue code on the _layout.cshtml page looks something like the code below. By instantiating Vue on the _layout.cshtml which is used by the whole site, Vue is only instantiated in a single place sitewide:
_layout.cshtml :
<script type="text/javascript">
var app = new Vue({
el: '#appTemplate',
mixins: mixinArray, //The page adds it's mixin to mixinArray before this part of the layout executes.
data: {
errorMsg: '' //used sitewide for error messages
//other data used sitewide
},
methods: {
//methods that need to be available in vue sitewide, examples below:
showBusy: function (html) {
//functionality to show the user that the site is busy with an ajax request.
},
hideBusy: function () {
//functionality to hide the busy spinner and messaging
}
},
created: function () {
//this method is particularly useful for initializing data.
}
});
</script>
What I have provided here paints a pretty clear picture of this non-traditional approach and it's benefits. However, since several people asked, I also wrote a related blog post: Using VueJs with ASP.NET Razor Can Be Great!
You can do this. Sometimes you're obliged to do it, if, like us, you're migrating an existing code base and you can't convert everything at once. And as Ron C says, it works well.
If you're starting a new project, you have the luxury of choosing. Reasons for favouring an SPA and no Razor would be...
Reactivity. SPA apps generally feel (much) more reactive. Initial renders are often served from cache, before the data arrives. On first load, all resources arrive in a bundle, in one request-response. There's no, or much less, request chaining.
Workflow. Webpack, bundling and hot reloads are great. You get production builds, with minification, compilation of Vue render functions, elimination of 404 style errors, js syntax errors are trapped. The cycle from introducing an error to discovering it is greatly reduced for many errors.
SPA universe. Routing, Vuex, this really is the way of the future.
Purity. Razor and Vue do similar things at the end of the day. If you mix them, you may have a hard time keeping your head straight.
You can now also lint the VueJS templates within the Razor views:
https://www.npmjs.com/package/razor-vue-lint
Great answers and content on this question! Just to add re the OP, the official Vue documentation expressly states that you can mix and match and do what you like with Vue and that it is designed to be used incrementally so I'd say if it fits what you're trying to do then it is NOT an automatic bad practice.

Resources