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

await useStoryblok() in NUXT 3 setup script does NOT actually await (RESOLVED) #155

Closed
nedas-dev opened this issue Jun 24, 2022 · 29 comments
Labels
bug [Issue] Something isn't working duplicate [Issue][PR] Already exists

Comments

@nedas-dev
Copy link

nedas-dev commented Jun 24, 2022

Hey guys, I have this issue - once I build and run nuxt 3 in production environment, await useStoryblok('home', { version: 'draft', }); does not actually await for the content, it gets rendered on client side only. And yes - ssr:true.
All other pages that retrieve from, let's say Shopify GraphQL API, does await and load content on server side.
Any ideas?

index.vue

<template>
  <StoryblokComponent v-if="story" :blok="story.content" />
</template>

<script setup>
  const story = await useStoryblok('home', { version: 'draft', });
</script>

This is my package.json

{
  "private": true,
  "scripts": {
    "build": "nuxt build",
    "start": "nuxt start",
    "dev": "nuxt dev",
    "generate": "nuxt generate",
    "preview": "nuxt preview"
  },
  "devDependencies": {
    "@nuxtjs/tailwindcss": "^5.0.4",
    "nuxt": "3.0.0-rc.1",
    "sass": "^1.51.0",
    "sass-loader": "^12.6.0"
  },
  "dependencies": {
    "@shopify/theme-addresses": "^4.1.1",
    "@storyblok/nuxt": "^4.0.1",
    "@vuelidate/core": "^2.0.0-alpha.41",
    "@vuelidate/validators": "^2.0.0-alpha.29",
    "@vueuse/core": "^8.7.5",
    "@vueuse/router": "^8.7.5",
    "axios": "^0.27.2",
    "form-data": "^4.0.0",
    "formidable": "^2.0.1",
    "js-cookie": "^3.0.1",
    "lazysizes": "^5.3.2",
    "multer": "^1.4.5-lts.1"
  }
}

@nedas-dev nedas-dev changed the title await useStoryblok() in nuxt 3 setup script DOES NOT AWAIT await useStoryblok() in NUXT 3 setup script does NOT actually await Jun 24, 2022
@danielmalmros
Copy link

I'm having the same issue.
When running Nuxt 3 with await useStoryblok() in production (docker image) I see content is rendered on client even when the ssr: true is set.

I would expect it to be rendered on the server side.
Any idea how to solve this?

@marcohofmann
Copy link

Have you checked if it is also requested on the server side? Because 2 have two requests.
How can i prevent the unneccessary second call on the client side when entering the page?
THX

@SebbeJohansson
Copy link
Contributor

I can confirm the same issue with await storyblokApi.get("cdn/stories", {}). The data is pulled on the server, but the components are not rendered on the server.

I think the issue is with the <StoryblokComponent v-if="story" :blok="story.content" /> rather then the actual pulling.

@danielmalmros can you confirm that you are also seeing the same behaviour if you display the content with {{ story.content }} instead of the component?

@nedas-dev
Copy link
Author

nedas-dev commented Jul 12, 2022

UPDATE

We got it resolved:
INSTEAD OF THIS
index.vue

<template>
  <StoryblokComponent v-if="story" :blok="story.content" />
</template>

<script setup>
  const story = await useStoryblok('home', { version: 'draft', });
</script>

We overwrote with this
index.vue

<template>
  <component :is="component" v-if="story" :blok="story.content" />
</template>

<script setup>
  const story = await useStoryblok('home', { version: 'draft', });
  const component = resolveComponent(story.content.component)
</script>

More info: https://v3.nuxtjs.org/guide/directory-structure/components/#dynamic-components

@nedas-dev nedas-dev changed the title await useStoryblok() in NUXT 3 setup script does NOT actually await await useStoryblok() in NUXT 3 setup script does NOT actually await (RESOLVED) Jul 12, 2022
@SebbeJohansson
Copy link
Contributor

@nedas-dev Holy shit thats a good workaround! Thanks for that fix!

Since mine is in a v-for I did it as follows:

<script async setup lang="ts">
/* Pull data using await storyblokApi.get("cdn/stories", {}) */

function resolveStoryBlockComponent(story) {
  if (!story) return null;
  return resolveComponent(story?.content?.component);
}
</script>

<template>
  <div class="blog-post-list">
    <component class="blog-post-list__entry" :class="entry.classes" v-for="entry in blogEntries"
      :is="resolveStoryBlockComponent(entry)" :blok="entry.content" :raw="entry" :key="entry._uid" />
  </div>
</template>

@mario-neuhold
Copy link

Thanks, that helped me a lot as well.
In my case "useStoryblok" returned a ref, so I had to access the storys content via the .value property: story.value.content

<script setup>
  const story = await useStoryblok('home', { version: 'draft', });
  const component = resolveComponent(story.value.content.component)
</script>

@Dawntraoz
Copy link
Contributor

Dawntraoz commented Nov 3, 2022

Hi @nedas-dev @danielmalmros @marcohofmann @SebbeJohansson @mario-neuhold I know it's been a long time since this issue, but have you seen the new composable that makes use of Nuxt's useAsyncData, useAsyncStoryblok, that's what was needed for Nuxt to work correctly with SSR and SSG. (Related issue #192)

Let me know if everything is ok nowadays, to close the issue, or if you need anything else!

@Dawntraoz Dawntraoz added duplicate [Issue][PR] Already exists bug [Issue] Something isn't working labels Nov 3, 2022
@SebbeJohansson
Copy link
Contributor

SebbeJohansson commented Nov 3, 2022

@Dawntraoz useAsyncStoryblok does not work correctly in dev mode.
image
image

Maybe I should open a separate issue.

Edit: I think I was confused back in november.

@SebbeJohansson
Copy link
Contributor

SebbeJohansson commented Nov 3, 2022

Actually, that issue might be coming from some kind of issue in nuxt. I am not sure. It suddenly stopped happening after resaving nuxt.config.ts. Weird stuff!

Edit: I think I was confused back in november.

@SebbeJohansson
Copy link
Contributor

SebbeJohansson commented Nov 3, 2022

The issue has to do with ssr: false apparently. You can see the issue here:
https://stackblitz.com/edit/github-blf1aq?file=composables%2FuseStoryblokFetch.ts,nuxt.config.ts

Edit: I think I was confused back in november.

@SebbeJohansson
Copy link
Contributor

Keep in mind that this issue is not only with async, but also with the normal useStoryblok composable.

@SebbeJohansson
Copy link
Contributor

SebbeJohansson commented Jan 28, 2023

Assuming that nuxt is in ssr
I can confirm that it looks like it is working now without the workaround.

With useAsyncStoryblok:
bloks are rendered fine on ssr.
image
With useStoryblok:
bloks are rendered fine on ssr, but obviously fetched both on the server and on the client.
image
image

@k16e-me
Copy link

k16e-me commented Jan 28, 2023

I do honestly believe this problem still exists. Well, at least for me, it does. I've upgraded Node, tried all the workaround, and it's only fine when ssr: false, which is not desirable in Nuxt since all the fetching now happens on the client side, defaulting to a blank SPA-like app. @SebbeJohansson has been gracious and invested enough in looking both at my code and a minimal StackBlitz I put up.

The bigger shame with the whole thing is its erratic nature. It would load fine sometimes and then would suddenly break, or sometimes just outright return 500 and, of course, that breaks everything.

Here's a link to my project (open), if anyone wants to take a look, and especially if I'm really goofing somewhere and woefully blinded to it myself ==> https://gitlab.com/k16e/c.food

Thanks.

@SebbeJohansson
Copy link
Contributor

@k16e-me not sure why yours isnt waiting for the result. might be something with how you store the result or that the slug is incorrect. This issue is specifically for the fact that before the storyblok components didn't render at all on ssr, which I know for a fact yours does.

I think you should open a new issue for the other stuff!

@alvarosabu
Copy link
Contributor

I do honestly believe this problem still exists

Hi @k16e-me could you please confirm for me if the bug is still happening to prioritize? Thank you
If so, is the https://gitlab.com/k16e/c.food still a valid reproduction?

@alvarosabu alvarosabu added the pending-author [Issue] Add Storyblok employee label Apr 25, 2023
@alvarosabu alvarosabu self-assigned this Apr 25, 2023
@thomascallahan
Copy link

thomascallahan commented Apr 30, 2023

@alvarosabu @k16e-me I believe this is still happening for me too but I have noticed something that might help -- I'm not sure if this is actually a problem with the Storyblok plugin (at least in my case) or if it's a Nuxt SSG/Vue Router problem.

I just started building a site and I'd really prefer to be full static delivery and not hit Storyblok at all. This is my first Nuxt 3 (built a few with Nuxt 2) site and second Storyblok site (the other used Astro).

After running nuxt generate, I can see the correct output in the static HTML files in the dist folder, including locally cached API call payload files, but in the browser after deploying to CloudFlare Pages or using npx http-server locally, I can see it still hitting the Storyblok API directly -- but only sometimes.

Because I was only seeing it sometimes, I spent some time clicking around and reloading trying to find a pattern. It looks like it's because of trailing slashes. My <NuxtLink> links do not include trailing slashes:

:to="'/' + slug" where slug might be "about" or "contact-us" or whatever.

While navigating the site using those links everything's fine. But if I reload while looking at any route that's not the homepage, for example /about, something adds a trailing slash making it /about/, and since that doesn't match a statically generated page it dynamically renders it, including the API call.

I've tried forcing trailing slashes off using various Nuxt configs and middlware with no success (I'd prefer not to have trailing slashes). The only solution I found was to add trailing slashes to my links so that when Nuxt prerenders the site, it creates matching pages. Then if I reload, there's a matching static file for that route.

This still leaves an issue if someone hits a link that doesn't include a trailing slash (for example saying to someone "go to website.com slash trial" they're going to type in website.com/trial with no trailing slash. So I still need to find a way to force it entirely one way or the other, although I'm not sure if that's even possible with SSG. I might need a CloudFlare redirect rule. And I need to figure out why trailing slashes are being added in the first place and turn that off. It must be Nuxt/Vue Router doing it since it happens both in CloudFlare and locally using http-server.

I found a discussion here which exactly describes what I'm seeing, but none of the proposed solutions seemed to work for me. And neither did any of the ones in this SO thread. Also just found this discussion, seems like this is a known issue with work in progress: nuxt/nuxt#15462 (comment)

@SebbeJohansson
Copy link
Contributor

@thomascallahan doing fully static has always been hard, but I have an example where I have gotten it to work, but not with the composables. I have a plan to look into this more, but life has gotten between those plans.

Here is a production url: https://sebbejohansson.com/blog/performance-improvements/
As you can see if you try loading it without trailing slash, it will redirect. That's the first key.
Here is the router options that I did: https://github.com/SebbeJohansson/sebbejohansson-front/blob/main/app/router.options.ts
Here is the page component that handles the fetching using my custom fetch-composable: https://github.com/SebbeJohansson/sebbejohansson-front/blob/main/pages/blog/%5Bslug%5D.vue
Here is the fetch-composable: https://github.com/SebbeJohansson/sebbejohansson-front/blob/main/composables/useStoryblokFetch.ts

This should allow for properly pre generated pages.

Alba at storyblok has plans to incorporate something similar into UseAsyncStoryblok so that should be possible to use instead.

@thomascallahan
Copy link

@SebbeJohansson Thanks for all of that. I tried a version of that in my project (had to modify it a little) and like other solutions I tried, NuxtLink>s break -- the address bar updates but new content does not load. And I still see API calls in the static version.

I think I made a mistake trying Nuxt 3, seems like SSG is just not ready in it yet, or at least not when combined with Storyblok. As much as I don't want to do this (I really don't like React or JSX) I may have to give up and use NextJS, I've had no problems generating truly static sites with it. Or maybe fall back to Nuxt 2 for now.

@SebbeJohansson
Copy link
Contributor

@thomascallahan nuxt3 with ssg works fine, so sounds like something is wrong in your version. Feel free to message me on discord and I'll see if I can help you.

@danbaechtold
Copy link

I found the reason and a solution.

useStoryblokBridge is in fact the problem and causes payload invalidation and reloading, instead of relying on the pre-generated payload.

To fix, instead of using useAsyncStoryblok, just fetch the story manually with useAsyncData and storyblokApi.get (as shown in readme).

Then, to have the bridge when actually editing in the visual editor, add this onMounted code:

onMounted(() => {
  if (!!route.query._storyblok) { // only load bridge in editor
    useStoryblokBridge(story.value.id, (evStory) => (story.value = evStory))
  }
})

@k16e-me
Copy link

k16e-me commented May 24, 2023

readme

@SebbeJohansson Did you notice? But then, @danbaechtold would you be able to show how useStoryblokBridge is causing this error (like in the code)?

Because even if your solution works, it's like going back where we're supposed to move away from. If Storyblok has written a shorthand that wraps up the manual process, going back to that feels like overwork/repeat code.

@SebbeJohansson
Copy link
Contributor

@danbaechtold can you confirm that you have the same issue when running useAsyncStoryblok? Could have sworn this works now.

@danbaechtold
Copy link

Yes, my solution is a workaround and I'd also prever to be able to use "useAsyncStoryblok".

I was able to reproduce it in a new nuxt app and to find the exact cause: It's only breaking when I use different "version" parameters based on NODE_ENV.

So, with this:
version: process.env.NODE_ENV !== 'production' ? 'draft' : 'published'

And then running "nuxt generate" and "nuxt preview, "useAsyncStoryblok" will fail, while my workaround will still work. It must be the bridge.
It's then obvious that data is beeing refetched when looking into the network tab (and the page even flickers and at first shows the initial (generated) payload before the bridge breaks the data by setting it to undefined).

Here is a simple Repro:
https://github.com/danbaechtold/nuxt3-storyblok-async

@k16e-me
Copy link

k16e-me commented May 24, 2023

Yes, my solution is a workaround and I'd also prever to be able to use "useAsyncStoryblok".

I was able to reproduce it in a new nuxt app and to find the exact cause: It's only breaking when I use different "version" parameters based on NODE_ENV.

So, with this: version: process.env.NODE_ENV !== 'production' ? 'draft' : 'published'

And then running "nuxt generate" and "nuxt preview, "useAsyncStoryblok" will fail, while my workaround will still work. It must be the bridge. It's then obvious that data is beeing refetched when looking into the network tab (and the page even flickers and at first shows the initial (generated) payload before the bridge breaks the data by setting it to undefined).

Here is a simple Repro: https://github.com/danbaechtold/nuxt3-storyblok-async

This makes a lot of sense. Would a rewrite be something Nuxt 3 is involved or is it solely from the Storyblok module end?

@SebbeJohansson
Copy link
Contributor

SebbeJohansson commented May 24, 2023

@danbaechtold I still dont understand why some people want to fetch version based on NODE_ENV. To me it makes so much more sense to fetch draft when in the editor and otherwise fetch from published.
Does the "good version" work with routing too, without prefetching?

Edit: Looking at your code, it seems like the main difference is the use of toRef. Do you know if that was the part that made it work for you?

@thomas-callahan-collibra

I still dont understand why some people want to fetch version based on NODE_ENV.

Previews without needing Storyblok access. I have stakeholders who needs one-time access to review draft content, or they forward those requests get forwarded to different people, and I can't reasonably provision Storyblok access for each of them. And others who need routine access so they might warrant user provisioning, but I can't imagine being able to get them to log in to Storyblok -- think execs who just want to be able to see the site without knowing anything at all about how it's built. (Security of the draft content is not an issue in my case).

I have a preview version of a site deployed in SPA mode on CloudFlare Pages so that content stakeholders can see the draft content without having to have Storyblok access. This is the same site I use for Storyblok's editor preview for content authors, but because it can be accessed outside of Storyblok and I want them to be able to navigate around the site normally while remaining in draft content mode, it has to be set in the build config rather than detecting if it's running in the editor or not at runtime.

And then production deployment is a second CF Pages site that does a static build from a Storyblok webhook instead of SPA.

I suppose I could dynamically detect if it's running in the editor AND check the domain in order to set this at runtime, but to me it was just easier to set an environment variable in CF Pages -- it's an environment detail. I'm actually just setting the content version name as an environment variable, like STORYBLOK_VERSION=draft or STORYBLOK_VERSION=publish and there's no conditional in the JS at all, I just use it directly: version: process.env.STORYBLOK_VERSION

@SebbeJohansson
Copy link
Contributor

@thomascallahan getting close to being off topic for this issue, but that makes so much sense. I've not needed that yet, but I do see that use case being a thing. I personally would rather put a cookie if needing to preview consistantly accross a site tho.

@danbaechtold
Copy link

@SebbeJohansson No, it is not the toRef that solves the problem, but the condition around "useStoryblokBridge":

onMounted(() => {
  if (!!query._storyblok) { // <-- this is the fix
    useStoryblokBridge(story.value.id, (evStory) => (story.value = evStory))
  }
})

If the condition is removed, it breaks. The error then is: TypeError: Cannot read properties of undefined (reading 'id'), and as far as I can tell it comes from the story.id part in the template.

PS, my reason to use NODE_ENV is that I like to develop new features outside of the editor. But if I then have for example to add a new Blok I'm developing, then obiously I need to add it to the (draft) content and prefer not to have to publish yet. Developing outside of the editor is easier in terms of space, dev tools, etc.

@Dawntraoz Dawntraoz removed the pending-author [Issue] Add Storyblok employee label Jul 17, 2023
@Dawntraoz
Copy link
Contributor

I will close this issue since people already help each other. If you have any problems with this topic, please create a new issue with a reproducible example.

For now, you can check the latest changes to the composable at #398. Please let me know your opinions and use cases from your side <3 Thanks for helping each other. I loved reading the thread and seeing people helping!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug [Issue] Something isn't working duplicate [Issue][PR] Already exists
Projects
None yet
Development

No branches or pull requests