Client-Only works as expected in Nuxt 3. Script code is not evaluated server-side! The post below is for Nuxt 2.
Common Problems With The Nuxt Client-Only Component
When I first started learning Nuxt, I thought the <client-only>
component was the jack of all trades. It's obvious right? If there is something you want to run in the browser, just wrap it in a <client-only>
component and then move on. It turns out, it isn't that simple.
TLDR: The <client-only>
component doesn't do what you think it does. Yes, it skips rendering your component on the server side, but it still gets executed!
Here is a simple example. Let's say the script below is a simple page using some kind of 3rd party library that requires window or document.
<template>
<div>
<client-only>
<jot-form/>
</client-only>
</div>
</template>
<script>
import JotForm from '@/components/jotForm'
export default {
components: {
JotForm
}
}
</script>
<template>
<iframe
id="JotFormIFrame-12345"
title="Cats"
onload="window.parent.scrollTo(0,0)"
allowtransparency="true"
allowfullscreen="true"
allow="geolocation; microphone; camera"
src="https://form.jotform.com/12345"
frameborder="0"
style="
min-width: 100%;
height:539px;
border:none;"
scrolling="no"
></iframe>
</template>
<script>
// client-only protects us right... (cough 'not really')
var ifr = document.getElementById("JotFormIFrame-12345");
if (window.location.href && window.location.href.indexOf("?") > -1) {
var get = window.location.href.substr(
window.location.href.indexOf("?") + 1
);
if (ifr && get.length > 0) {
var src = ifr.src;
src = src.indexOf("?") > -1 ? src + "&" + get : src + "?" + get;
ifr.src = src;
}
}
</script>
Even though jotForm.vue is nested within a <client-only>
component, IT WILL STILL EXPLODE SERVER SIDE!
Solutions:
- lazy load your client component
const form = () => import('...')
- move your client code into the
mounted()
hook - wrap your client code inside an
if(process.client)
statement
<script>
export default {
// By moving the code that needs window & document to mounted, now it will
// only be executed client-side
mounted() {
var ifr = document.getElementById("JotFormIFrame-12345");
if (window.location.href && window.location.href.indexOf("?") > -1) {
var get = window.location.href.substr(
window.location.href.indexOf("?") + 1
);
if (ifr && get.length > 0) {
var src = ifr.src;
src = src.indexOf("?") > -1 ? src + "&" + get : src + "?" + get;
ifr.src = src;
}
}
}
}
</script>
Problem Libraries
Sometimes when you use vue libraries with SSR (Nuxt) you get window or document errors just by importing them! This means they aren't SSR compatible and you should move their import into a client-side plugin.
Summary
At this point, you may be thinking; "Josh, if I have to use other methods to protect the server from window or document, why would I need <client-only>
at all?"
Super good question, I'm glad you asked.
I would say for a vast majority of people, they wouldn't want to use it at all. Me, I use it along with one of the 3 methods above just to keep all of my third party browser-side components totally client-side. But hey, I'm a little weird.
That's all for now. Feel free to drop me a line on Twitter and tell me what you think!