Jump to content
Search Community

Using Flip in Nuxt results in breaking error `document is not defined`

katerlouis test
Moderator Tag

Recommended Posts

Heyho partypeople,

 

I don't know how to recreate this issue with CodePen, since I'm using Nuxt. And adding my gsap-bonus files to codesandbox also doesn't look like a good idea 😕

 

So I'll have to go the old fashioned way:

 

I'm making a Nuxt (VueJS) app and have gsap bonus (shockingly green) installed as suggested: `$ npm install ./gsap-bonus.tgz`.

In my simple test page component `src/pages/portal.vue` I install Flip like any other plugin:

 

```js

<script>

import { gsap } from 'gsap';
import { Flip } from 'gsap/dist/Flip';

gsap.registerPlugin(Flip);

 

export default {

 // Vue magic here

```

 

When I navigate client side from `/` to `/portal`, it works as expected. But when I hard refresh on `/portal` I get a breaking error saying:

document is not defined – Apparently the issue is caused by this:

 

	  Flip.register = function register(core) {
	    gsap = core;

	    _setDoc(document.body);

	    _toArray = gsap.utils.toArray;
	    _closestTenth = gsap.utils.snap(0.1);
	  };

 

I've already tried installing plugins by importing the ES modules instead of UMD modules, which works on client side navigation, but again not on hard refresh. Then I get: Cannot use import statement outside a module

 

Why is Flip dependent on `document` during import? And how would you suggest getting Flip to work with a Nuxt component?

 

 

Thanks!

 

Link to comment
Share on other sites

Thanks for the quick reply!

The thread you linked comes to the same conclusion as I have, importing UMD modules, right?

But as for the error `doucment is not defined` – importing Flip on client side would result in a big delay, right?! How could you even import with conditionals? That sounds sketchy to me :D

 

And how would transpiling GSAP solve the the document issue? Could you explain the two options a bit deeper, please? :)

 

EDIT: To be clear, I do not use Flip yet– all I'm doing is registering it, which already results in the error.

Link to comment
Share on other sites

33 minutes ago, kreativzirkel said:

Why is Flip dependent on `document` during import?

It's not. In fact, that's one of the main reasons we reworked the internals in GSAP back in 2019 (when we rewrote everything to go from GSAP 2 to GSAP 3), so that dependencies on the document/body/window could be delayed - that's where gsap.registerPlugin() came from. The reality is that in order to accomplish certain things, Flip NEEDS access to the document/body/window but instead of hard-coding those into the tool instantiation itself, we tucked those into a method that gets triggered when you gsap.registerPlugin() that plugin. This allows it to accommodate SSR and things like Nuxt by letting you just wait to gsap.registerPlugin() until the document/body/window exist. 

 

So all you need to do is make your code run (including the gsap.registerPlugin()) when the document/body/window exist. Don't just tuck it right under the import methods. 

 

Does that clear things up? 

  • Like 2
Link to comment
Share on other sites

I am terribly sorry, the installation docs mention server side rendering and nuxt explicitly .. 

 

So the suggestion is to only register the plugins on the client using `process.client` but import the necessary scripts "as usual". That makes sense to me now. 

I'm still wondering why this hasn't been a problem up to here; I haven't encountered this issue yet and have always registered right after import (apparently on server side?)

 

And what exactly happens when I "transpile" gsap? 

Would I still need to add the "process.client" conditional?

Link to comment
Share on other sites

Interesting. 

 

So what exactly happens when you transpile gsap and why do I not need to wait for `process.client` once transpiled? 

I'm honestly also not very firm with what happens under the hood when doing SSR. I just realized that the development server also already does SSR and that SSR isn't just happening when you run your final SSR build on the server.

 

Why `if (process.client)` even works is also a mystery to me :D – My guess would be the entire <script> section gets called both on the server and then again on the client? So "wait for process.client" isn't really the case; there's no waiting happening – If that's too much off-topic just tell me and I shut up :D 

 

The questions about transpilation and why transpilation wouldn't require to check for `process.client` remain though 😎 

Link to comment
Share on other sites

9 minutes ago, kreativzirkel said:

So what exactly happens when you transpile gsap and why do I not need to wait for `process.client` once transpiled? 

I suspect this whole transpiling thing may be muddying the waters. That typically just means taking ES Modules (more modern JS) and translating it into standard ES5 code that all browsers understand. I don't think it's related to the timing issue with attempting to register a plugin when the browser environment doesn't exist yet. 

 

I'm not familiar with Nuxt specifically or how it works under the hood, but let's say, for example, that it converts all your imports (ES Module) code into ES5 server-side, so there is no browser yet...no window, no document, nothing like that. Then it ships it to the browser where the code gets executed (again?). NOW there's a window/document. Or maybe there's a step that happens on the browser side before it's granted access to the browser environment - I don't know, but the point is that at the time it has no access to window/document/body it is executing gsap.registerPlugin() and it burps, saying "dude, I need the window/document...but there ain't none!" 

 

I believe process.client is a way of checking to see if the browser environment is available at that time. 

  • Like 1
Link to comment
Share on other sites

Okay, very interesting.

 

Would you suggest to always wrap registering plugins with `if process.client`?

 

Or maybe somebody more familiar with Nuxt knows a way to install gsap and the plugins I want globally so that I don't need to import them explicitly in my components. It feels like Nuxt says bye to tree-shaking anyways and I've gotten quite fond of not having to import components etc. – gsap is in fact the only thing I still import by hand 😮

 

As for the transpiling– I'm pretty sure on build everything gets squished into ES5 anyways?

Link to comment
Share on other sites

3 minutes ago, katerlouis said:

Would you suggest to always wrap registering plugins with `if process.client`?

It wouldn't just be the registering of the plugin - none of your GSAP-related code should be executed until the plugin is registered. You wouldn't want to run it anyway if it's not in the browser, right? (it'd be weird to animate stuff that nobody can see :)

 

I'm not a Nuxt guy, so I really can't tell you what that system requires in particular but I imagine that you should either tap into its lifecycle events somehow to ensure that the code only runs when it's in the browser, or wrap it all in "if (process.client) {...}" - perhaps you should ask the Nuxt community. If you get any answers, it'd be swell if you posted here for others to learn too. 👍

  • Like 1
Link to comment
Share on other sites

If you want gsap to be global, make a file in your plugins directory, import the gsap modules you want, then add

 

gsap.install(window);

 

Then in your nuxt config, import that file in the plugins section.

 

plugins: [
  { src: '~/plugins/my-gsap-imports.js', mode: 'client' }, // only on client side
]

 

You still may have to do process.client type of checks before running any gsap code. 

 

  • Like 2
Link to comment
Share on other sites

Thanks guys!

My "actual GSAP code" is only used by the `<transition>` component or in the mounted() hook, so only when the DOM is actually ready. Never thought about doing that for registering plugins, though.

 

It feels a bit sketchy to install it globally like this :D – but Blake is never wrong ^^

Wouldn't the linter scream "gsap is not defined" upon usage? And how would I install Flip globally with this method? 

Link to comment
Share on other sites

15 minutes ago, katerlouis said:

It feels a bit sketchy to install it globally like this

 

It was intentionally designed that way, so not sketchy, just undocumented.

 

Just think of it as a feature.

 

16 minutes ago, katerlouis said:

but Blake is never wrong

 

So true 😉

 

14 minutes ago, katerlouis said:

My "actual GSAP code" is only used by the `<transition>` component or in the mounted() hook, so only when the DOM is actually ready. Never thought about doing that for registering plugins, though.

 

That should be fine. The only time you really need to be concerned about doing that process.client check is early in the lifecycle, like inside a created method.

 

Also... just messing around. Don't click too fast.

See the Pen fae1daff9f042277e3d54e25af9c88f7 by osublake (@osublake) on CodePen

 

I would definitely like to see proper FLIP functionality with Vue.

 

 

  • Like 2
Link to comment
Share on other sites

What about the linter whining when installing gsap globally?

 

Yeah, I was also exited to dive into Flip, just to realize that Vue-portal for instance (or basically any other conditional rendering etc.) is actually dismounted and mounted back in again and therefore not compatible with Flip (as far as I can tell right now) – I'd love to use the recorded flip state on "different" elements (basically the same element, but technically a new one :( )

Link to comment
Share on other sites

4 hours ago, katerlouis said:

dismounted and mounted back in again and therefore not compatible with Flip

Flip should be compatible with things being added and removed from the DOM if it's set up properly. It might require using specific hooks/callbacks appropriately. If you set up a minimal demo of the situation perhaps we can provide some input.

Link to comment
Share on other sites

 

10 minutes ago, ZachSaucier said:

Flip should be compatible with things being added and removed from the DOM if it's set up properly. It might require using specific hooks/callbacks appropriately. If you set up a minimal demo of the situation perhaps we can provide some input.

Sounds good! I'll make a new topic for that.

Link to comment
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...