Why is my `client-only` component in nuxt complaining that `window is not defined`?
Solution 1
I found a way that works though I'm not sure how. In the parent component, you move the import statement inside component declarations.
<template>
<client-only>
<map/>
</client-only>
</template>
<script>
export default {
name: 'parent-component',
components: {
Map: () => if(process.client){return import('../components/Map.vue')},
},
}
</script>
Solution 2
<template>
<client-only>
<map/>
</client-only>
</template>
<script>
export default {
name: 'parent-component',
components: {
Map: () =>
if (process.client) {
return import ('../components/Map.vue')
},
},
}
</script>
The solutions above did not work for me.
Why? This took me a while to find out so I hope it helps someone else.
The "problem" is that Nuxt automatically includes Components from the "components" folder so you don't have to include them manually. This means that even if you load it dynamically only on process.client it will still load it server side due to this automatism.
I have found the following two solutions:
-
Rename the "components" folder to something else to stop the automatic import and then use the solution above (process.client).
-
(and better option IMO) there is yet another feature to lazy load the automatically loaded components. To do this prefix the component name with "lazy-". This, in combination with will prevent the component from being rendered server-side.
In the end your setup should look like this Files:
./components/map.vue
./pages/index.html
index.html:
<template>
<client-only>
<lazy-map/>
</client-only>
</template>
<script>
export default {
}
</script>
Solution 3
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!
https://deltener.com/blog/common-problems-with-the-nuxt-client-only-component/
Solution 4
Here is how I do it with Nuxt in Universal mode: this will: 1. Work with SSR 2. Throw no errors related to missing marker-images/shadow 3. Make sure leaflet is loaded only where it's needed (meaning no plugin is needed) 4. Allow for custom icon settings etc 5. Allow for some plugins (they were a pain, for some reason I thought you could just add them as plugins.. turns out adding them to plugins would defeat the local import of leaflet and force it to be bundled with vendors.js)
Wrap your template in <client-only></client-only>
<script>
let LMap, LTileLayer, LMarker, LPopup, LIcon, LControlAttribution, LControlZoom, Vue2LeafletMarkerCluster, Icon
if (process.client) {
require("leaflet");
({
LMap,
LTileLayer,
LMarker,
LPopup,
LIcon,
LControlAttribution,
LControlZoom,
} = require("vue2-leaflet/dist/vue2-leaflet.min"));
({
Icon
} = require("leaflet"));
Vue2LeafletMarkerCluster = require('vue2-leaflet-markercluster')
}
import "leaflet/dist/leaflet.css";
export default {
components: {
"l-map": LMap,
"l-tile-layer": LTileLayer,
"l-marker": LMarker,
"l-popup": LPopup,
"l-icon": LIcon,
"l-control-attribution": LControlAttribution,
"l-control-zoom": LControlZoom,
"v-marker-cluster": Vue2LeafletMarkerCluster,
},
mounted() {
if (!process.server) //probably not needed but whatever
{
// This makes sure the common error that the images are not found is solved, and also adds the settings to it.
delete Icon.Default.prototype._getIconUrl;
Icon.Default.mergeOptions({
// iconRetinaUrl: require('leaflet/dist/images/marker-icon-2x.png'), // if you want the defaults
// iconUrl: require('leaflet/dist/images/marker-icon.png'), if you want the defaults
// shadowUrl: require('leaflet/dist/images/marker-shadow.png') if you want the defaults
shadowUrl: "/icon_shadow_7.png",
iconUrl: "/housemarkerblue1.png",
shadowAnchor: [10, 45],
iconAnchor: [16, 37],
popupAnchor: [-5, -35],
iconSize: [23, 33],
// staticAnchor: [30,30],
});
}
},
And there's proof using nuxt build --modern=server --analyze
https://i.stack.imgur.com/kc6q4.png
yam
Updated on June 05, 2022Comments
-
yam almost 2 years
I have Vue SPA that I'm trying to migrate to nuxt. I am using
vue2leaflet
in a component that I enclosed in<client-only>
tags but still getting an error from nuxt saying thatwindow is not defined
.I know I could use
nuxt-leaflet
or create a plugin but that increases the vendor bundle dramatically and I don't want that. I want to import the leaflet plugin only for the components that need it. Any way to do this?<client-only> <map></map> </client-only>
And the
map
component:<template> <div id="map-container"> <l-map style="height: 80%; width: 100%" :zoom="zoom" :center="center" @update:zoom="zoomUpdated" @update:center="centerUpdated" @update:bounds="boundsUpdated" > <l-tile-layer :url="url"></l-tile-layer> </l-map> </div> </template> <script> import { LMap, LTileLayer, LMarker, LFeatureGroup, LGeoJson, LPolyline, LPolygon, LControlScale } from 'vue2-leaflet'; import { Icon } from 'leaflet'; import 'leaflet/dist/leaflet.css'; // this part resolve an issue where the markers would not appear delete Icon.Default.prototype._getIconUrl; export default { name: 'map', components: { LMap, LTileLayer, LMarker, LFeatureGroup, LGeoJson, LPolyline, LPolygon, LControlScale }, //...