Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

'Window is not defined' where do I put client-side only scripts? #30

Closed
7ammer opened this issue Dec 2, 2016 · 29 comments
Closed

'Window is not defined' where do I put client-side only scripts? #30

7ammer opened this issue Dec 2, 2016 · 29 comments

Comments

@7ammer
Copy link

7ammer commented Dec 2, 2016

I'm trying to run a-frame in one of my vue pages but I'm getting an error in my browser:
ReferenceError: window is not defined

I'm guessing this is because nuxt is trying to run the aframe module while rendering the page on the server.

<template>
    <div>
        <a-scene fog="type: linear; color: #FFEBDE; far:190"  debug>
            <a-entity>
                <a-collada-model src="/obj/banner.dae"></a-collada-model>
                <a-ring color="teal" radius-inner="1" radius-outer="2"></a-ring>
            </a-entity>

            <a-entity position="0 -1 10">
                <a-camera>
                    <a-cursor color="#2E3A87"></a-cursor>
                </a-camera>
            </a-entity>
            <a-entity camera="userHeight: 1.6" look-controls></a-entity>
        </a-scene>
    </div>
</template>

<script>
import 'aframe';

export default {
    mounted() {

    }
}
</script>

This question is available on Nuxt.js community (#c30)
@alexchopin
Copy link
Member

alexchopin commented Dec 2, 2016

Hi,
Yes, this is exactly due to the server-side rendering.
If you need to specify that you want to import a ressource only on the client-side inside, you need to you process.browser variable.

.vue file

<script>
if (process.browser) {
  require('aframe')
}
export default {

}
</script>

nuxt.config.js

  build: {
    vendor: ['aframe']
  }

Thanks for your report, the documentation of the website will be completed soon.

@7ammer
Copy link
Author

7ammer commented Dec 2, 2016

Ah ha, brilliant! Thanks!

@7ammer
Copy link
Author

7ammer commented Dec 2, 2016

Hmm a-frame doesn't play nice with vue. It doesn't load properly.

@dzcpy
Copy link

dzcpy commented Dec 2, 2016

I have experinced similar issues as well. I was trying to import a vue UI framework element-ui. However, it references window object and several native browser functions like addEventListener etc., so it breaks the server rendering. I'm thinking if there is any way to polyfill it somehow. Because for such kind of UI framework, if we cannot render it on server side, it doesn't quite make sense and is probably not usable for SEO purpose.

I've also treid next.js with antd ( a UI framework built on React.js which similar to element-ui ) and it worked almost perfectly ( except that next.js is using glamor as a css-in-js solution which is pretty hacky). When I have time I'll dig more for the reason why it's working in next.js but not nuxt.js.

@Atinux
Copy link
Member

Atinux commented Dec 2, 2016

I think that every vue plugin should be able to work both client-side and server-side since the version 2.0 of Vue.js.

I asked the team behind element-ui but they said it's not their priority, but I don't think it will be complicated to make it compatible with server-side rendering, since we will only have to add if statement where a client-side only function is called.

@andyhu what library are you talking about which is working with next.js but not nuxt.js?

@dzcpy
Copy link

dzcpy commented Dec 2, 2016

@Atinux I mean I'm pretty sure that antd has some window object references as well. But when I tried it, it did work with next.js. However it probably because that antd is more modular and I missed the browser specific part of the variable references. Anyway I'm going to test more.

Update: I've found that in antd it's using if (typeof window !== 'undefined') to check if it's in the browser. So as you said, it's the plugin's issue and it shouldn't directly reference window object without envirument detection. element-ui IMO is a bit buggy.

@Atinux
Copy link
Member

Atinux commented Dec 2, 2016

I hope they will add theses check so element-ui could be used on the server-side too and render the proper HTML code!

I'm closing this issue since the question has been answer by @alexchopin by using process.BROWSER_BUILD.

@Atinux Atinux closed this as completed Dec 2, 2016
@zspecza
Copy link

zspecza commented Dec 6, 2016

I know this issue is closed and all, but I just want to point out an alternate solution - Vue's lifecycle hooks. All hooks except for beforeCreate and created do not even run on the server. If you're doing any client-side manipulation or inspection and referencing built-in browser globals like window or document, then you should do those things in these hooks. This solution removes the need to inspect environment variables.

@lmj0011
Copy link

lmj0011 commented Jan 27, 2017

Hi,
Yes, this is exactly due to the server-side rendering.
If you need to specify that you want to import a ressource only on the client-side inside, you need to you process.BROWSER_BUILD variable.[...]

why is this still not in the docs? I followed what was there and was still having issues importing a vue plugin. this solution cleared things up

the plugin in question: https://github.com/Wanderxx/vue-fullcalendar

@alexchopin
Copy link
Member

Documentation

@dkushner
Copy link

I have a follow up question that is a bit relevant to this issue and didn't want to clutter up the project by opening another one. I have code that needs to query browser-specific media and streaming capabilities. Where would that code best be located such that it is guaranteed to run when the component appears on the client side (whether it has previously been rendered by the server or not). I imagine this is also an issue for anyone trying to integrate with WebGL and other WebAPI technologies. Anyone have some advice?

@Atinux
Copy link
Member

Atinux commented Jul 11, 2017

@dkushner we have a special option for JS to be run only on client-side with ssr: false option: https://nuxtjs.org/guide/plugins#client-side-only

@dkushner
Copy link

@Atinux, perfect thanks! Just happened to miss that portion of the docs.

@dkushner
Copy link

@Atinux, actually I'm sorry it looks like that documentation is explicitly for Vue plugins. Do I need to wrap every external library I use as a Vue plugin in order to leverage this? For instance THREE and SocketIO will both rely on UA-specific objects being present like document and window. So do each of these libraries then become a separate plugin?

@lmj0011
Copy link

lmj0011 commented Jul 17, 2017

@dkushner yep

@dotnetCarpenter
Copy link

Currently, nuxt.js 1.0.0-rc11, it is process.browser and not process.BROWSER_BUILD inside a component.

@yun-cn
Copy link

yun-cn commented Nov 30, 2017

Vue-FullCalendar
app/plugins/vue-fullcalendar.js

import Vue from 'vue'

if (process.browser) {
  window.onNuxtReady(() => {
    const VueFullCalendar = require('vue-fullcalendar')
    Vue.use(VueFullCalendar)
  })
}

@MontageD
Copy link

This is the best answer I've ever seen. Thanks

@andrewharvey
Copy link

I'm closing this issue since the question has been answer by @alexchopin by using process.BROWSER_BUILD.

How would that work if we are using import instead of require? If I put my import inside the if statement I get:

NuxtServerError Module build failed: SyntaxError: 'import' and 'export' may only appear at the top level

@Atinux
Copy link
Member

Atinux commented Apr 13, 2018

You have to use require at the moment @andrewharvey and ask for the library author to add SSR support (by giving him this link if it's a Vue.js library: https://ssr.vuejs.org/en/universal.html)

@andrewharvey
Copy link

You have to use require at the moment @andrewharvey and ask for the library author to add SSR support (by giving him this link if it's a Vue.js library: https://ssr.vuejs.org/en/universal.html)

Thanks @Atinux. I also found I can use import by moving my code out from the page into a component and use ssr: false when loading that plugin in the nuxt.config.js.

@sudhir600
Copy link

sudhir600 commented Apr 19, 2018

@yun313350095 I tried process.browser but it didn't work. However, process.BROWSER_BUILD is working fine for me.

But is this real solution? I mean, I have to put this if block every method, where I want to something for client-side interaction.

@andrewharvey, for i18n plugin, i did same changes like nuxt.config.js -
{ src: '~plugins/i18n.js', ssr: false },
but now i am getting error as fallbackLocale is undefined.

@mxmaxime
Copy link

Hello, I'm very confused and having really bad times 😞

What if, I have to add some components after the first contentful paint ?

I've something like this:

    <no-ssr>
      <div id="post-footer">

        <post-share/>

        <div class="post-footer-suggestions" v-if="suggestionToShow">
          <p class="h2">ToRead</p>

          <post-suggestion/>

        </div>

      </div>
    </no-ssr>

But I can't figure out how to load these components in front...

// vue instance
components: {
  // What I have to do?
}

Any help would be appreciated

@sudhir600
Copy link

@EmixMaxime

<script>
import postShare from '~/components/anotherFolder/postShare.vue'

export default {
      components: {
           postShare
      }
</script>

in HTML,

<div id="post-footer">
        <postShare />
</div>

Hope this will help

@mxmaxime
Copy link

mxmaxime commented Apr 25, 2018

@sudhir600 Hum, that's loads the component in the server side, I have the window undefined error.

@pbastowski
Copy link

pbastowski commented Jun 7, 2018

You have to lazy-import your components only when the code is running on the client, like this:

<script>
export default {
      components: {
           postShare: process.client && import('~/components/anotherFolder/postShare.vue')
      }
</script>

@somebodytolove
Copy link

Thanks @Atinux. I also found I can use import by moving my code out from the page into a component and use ssr: false when loading that plugin in the nuxt.config.js.

@andrewharvey could you provide more details or an example of this implementation using the import statement?

@ChristophAnastasiades
Copy link

The only way I found to make scripts load only on the client side is to require them in one of the hooks that are not executed on the server side.

So for example with mapbox-gl you first set a parameter mapboxgl into the data () object. Then inside your hook (for example mounted ()) you require mapbox-gl and assign it to the new parameter.

<script>
export default {
  data () {
    return {
      mapboxgl: null
    }
  },
  mounted () {
    this.maboxgl = require('mapbox-gl')
  }
}
</script>

@lock
Copy link

lock bot commented Oct 31, 2018

This thread has been automatically locked since there has not been any recent activity after it was closed. Please open a new issue for related bugs.

@lock lock bot locked as resolved and limited conversation to collaborators Oct 31, 2018
@danielroe danielroe added the 2.x label Jan 18, 2023
obsfeil pushed a commit to obsfeil/nuxt that referenced this issue Jul 18, 2023
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests