Closed nedas-dev closed 1 year ago
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?
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
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?
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 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>
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>
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 https://github.com/storyblok/storyblok-nuxt/issues/192)
Let me know if everything is ok nowadays, to close the issue, or if you need anything else!
~@Dawntraoz useAsyncStoryblok does not work correctly in dev mode.~
~Maybe I should open a separate issue.~
Edit: I think I was confused back in november.
~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.
~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.
Keep in mind that this issue is not only with async, but also with the normal useStoryblok composable.
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. With useStoryblok: bloks are rendered fine on ssr, but obviously fetched both on the server and on the client.
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.
@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!
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 @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: https://github.com/nuxt/nuxt/issues/15462#issuecomment-1407374859
@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.
@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.
@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.
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))
}
})
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.
@danbaechtold can you confirm that you have the same issue when running useAsyncStoryblok? Could have sworn this works now.
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
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?
@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?
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
@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.
@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.
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 https://github.com/storyblok/storyblok-nuxt/pull/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!
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
This is my package.json