N
Nuxt3mo ago
ap

Page transitions occur after content has loaded between pages that re-use components

I am experiencing an issue that has been posted about before by @brick : https://discord.com/channels/473401852243869706/1233232719228567623 I currently have a site with page transitions that are working great. However, there is one instance where that is not the case. This is when navigating from one /work/handle page to a different /work/handle page. These pages all use a <VideoComponent/> that displays a video. Below the video is a grid of other work that user can navigate to. When a user clicks on another video the <VideoComponent/> updates to show the new video and THEN the page transition starts. I need the page transition to happen before the content changes. How can I achieve this?
7 Replies
kapa.ai
kapa.ai3mo ago
Hi @ap. I'm kapa.ai, your friendly support bot. I will try to answer your question by searching through the knowledge base. :wave: ---------------------- Based on the knowledge sources provided, I can offer some insights into your issue with page transitions in Nuxt 3, though there isn't a direct solution to your specific problem in the given information. The behavior you're experiencing, where the content changes before the transition occurs, is related to how Nuxt 3 handles page transitions and component updates. This is different from Nuxt 2's behavior with asyncData, where the URL would only change after all data fetching was complete Github Issue (https://github.com/nuxt/nuxt/issues/29535). In Nuxt 3, by default, the URL changes before the data for the new page has finished fetching. This can lead to situations where components update their content before the page transition begins, which seems to be what you're experiencing with your <VideoComponent/>. There are a few approaches you might consider to address this: 1. View Transitions API (Experimental): Nuxt 3 has experimental support for the View Transitions API, which might provide a solution to your problem. This API allows for more control over transitions between unrelated elements on different pages Nuxt Docs (https://nuxt.com/docs/getting-started/transitions#view-transitions-api-experimental). However, it's important to note that this is still experimental and may have some limitations. 2. Custom JavaScript Hooks: You could try using custom JavaScript hooks in your page transition to control when the content updates. This might allow you to delay the content change until after the transition begins Nuxt Docs (https://nuxt.com/docs/getting-started/transitions#javascript-hooks). 3. Dynamic Transitions: You might be able to use dynamic transitions with middleware to control how the transition behaves based on the pages you're navigating between Nuxt Docs (https://nuxt.com/docs/getting-started/transitions#dynamic-transitions). However, it's important to note that there are some known issues with transitions in Nuxt 3, especially when dealing with nested pages, layouts, or async components Github Pull Request (https://github.com/nuxt/nuxt/pull/20092). Your situation with reused components across different pages might fall into this category. Unfortunately, the knowledge sources don't provide a direct solution to your specific issue. You might need to experiment with different approaches or consider raising this as an issue on the Nuxt GitHub repository for more tailored assistance. ---------------------- Note: you can ask me a follow up question by @ mentioning me again :speech_balloon: ----------------------
kapa.ai
kapa.ai3mo ago
<@450508829449256960> Kapa.ai is still learning and improving, please let me know how I did by reacting below.
brick
brick3mo ago
@ap how are you loading the data for these components? I am loading my data through Pinia, and in order to fix the issue I previously had I used LazyAsyncData and a fallback template. This combination of things fixed the issue I had. I'm happy to look at your code more if you can provide any more information about it. Here is a quick example of what my component looks like (using posts as an example)
<script setup lang="ts">
// define props
const props = defineProps(["tag"]);

// local state for posts (to help with component swapping)
const posts = ref();

// init stores
const postsStore = usePosts();

// fetch data
await useLazyAsyncData(async () => {
await postsStore.getPosts(props.tag);
posts.value = postsStore.posts;
});
</script>

<template>
<div>
<template #fallback>
<LoadingBlock />
</template>
<div v-for"post in posts">
{{ post }}
</div>
</div>
</template>
<script setup lang="ts">
// define props
const props = defineProps(["tag"]);

// local state for posts (to help with component swapping)
const posts = ref();

// init stores
const postsStore = usePosts();

// fetch data
await useLazyAsyncData(async () => {
await postsStore.getPosts(props.tag);
posts.value = postsStore.posts;
});
</script>

<template>
<div>
<template #fallback>
<LoadingBlock />
</template>
<div v-for"post in posts">
{{ post }}
</div>
</div>
</template>
ap
apOP3mo ago
Hey @brick the data is being fetched from Sanity. This is how my page is structured:
<template>
<div id="work-page">
<div>
<template v-if="!pending && videoData">
<div>
<div class="video-area">
<VideoComponent
:videoURL="videoData?.asset?.videoURL"
:videoPoster="videoData?.asset?.image"
:showControls="true"
/>
</div>
<VideoGrid :videos="videoData.editor?.work"/>
</div>
</template>
</div>
</div>
</template>

<script setup>
import videoQuery from '../lib/sanity/queries/video';

const sanity = useSanity ();
const route = useRoute ();

const { pending, data: videoData } = await useAsyncData('videoData', async () => {
try {
const data = await sanity.fetch(videoQuery(route.params.handle));
return data;
} catch (error) {
console.log(error);
return {
isError: true
}
}
});

onBeforeRouteUpdate ((to, from) => {
if (to.name === from.name){
console.log('SAME ROUTE!!!')
}
});
</script>
<template>
<div id="work-page">
<div>
<template v-if="!pending && videoData">
<div>
<div class="video-area">
<VideoComponent
:videoURL="videoData?.asset?.videoURL"
:videoPoster="videoData?.asset?.image"
:showControls="true"
/>
</div>
<VideoGrid :videos="videoData.editor?.work"/>
</div>
</template>
</div>
</div>
</template>

<script setup>
import videoQuery from '../lib/sanity/queries/video';

const sanity = useSanity ();
const route = useRoute ();

const { pending, data: videoData } = await useAsyncData('videoData', async () => {
try {
const data = await sanity.fetch(videoQuery(route.params.handle));
return data;
} catch (error) {
console.log(error);
return {
isError: true
}
}
});

onBeforeRouteUpdate ((to, from) => {
if (to.name === from.name){
console.log('SAME ROUTE!!!')
}
});
</script>
I have tried playing around a bit with onBeforeRouteUpdate to really pinpoint when I need this more custom action to happen as navigating from this page to a page without the <VideoComponent/> works great and as expected. Any help is much appreciated !
brick
brick3mo ago
Can you try putting the videoData into a local ref and loading from that in your template?
<template>
<div id="work-page">
<div>
<template v-if="!pending && localVideoData"> // ALSO UPDATE THIS TO LOCAL
<div>
<div class="video-area">
<VideoComponent
:videoURL="localVideoData?.asset?.videoURL" // ALSO UPDATE THIS TO LOCAL
:videoPoster="localVideoData?.asset?.image" // ALSO UPDATE THIS TO LOCAL
:showControls="true"
/>
</div>
<VideoGrid :videos="videoData.editor?.work" />
</div>
</template>
</div>
</div>
</template>

<script setup>
import videoQuery from "../lib/sanity/queries/video";

const sanity = useSanity();
const route = useRoute();

const localVideoData = ref() // <-- ADD THIS

const { pending, data: videoData } = await useAsyncData(
"videoData",
async () => {
try {
const data = await sanity.fetch(videoQuery(route.params.handle));
return data;
} catch (error) {
console.log(error);
return {
isError: true,
};
}
}
);

// DIRECTLY ASSIGN
localVideoData.value = videoData

// OR TRY WATCHING?
watch(videoData, (newValue) => {
localVideoData.value = newValue
})

onBeforeRouteUpdate((to, from) => {
if (to.name === from.name) {
console.log("SAME ROUTE!!!");
}
});
</script>
<template>
<div id="work-page">
<div>
<template v-if="!pending && localVideoData"> // ALSO UPDATE THIS TO LOCAL
<div>
<div class="video-area">
<VideoComponent
:videoURL="localVideoData?.asset?.videoURL" // ALSO UPDATE THIS TO LOCAL
:videoPoster="localVideoData?.asset?.image" // ALSO UPDATE THIS TO LOCAL
:showControls="true"
/>
</div>
<VideoGrid :videos="videoData.editor?.work" />
</div>
</template>
</div>
</div>
</template>

<script setup>
import videoQuery from "../lib/sanity/queries/video";

const sanity = useSanity();
const route = useRoute();

const localVideoData = ref() // <-- ADD THIS

const { pending, data: videoData } = await useAsyncData(
"videoData",
async () => {
try {
const data = await sanity.fetch(videoQuery(route.params.handle));
return data;
} catch (error) {
console.log(error);
return {
isError: true,
};
}
}
);

// DIRECTLY ASSIGN
localVideoData.value = videoData

// OR TRY WATCHING?
watch(videoData, (newValue) => {
localVideoData.value = newValue
})

onBeforeRouteUpdate((to, from) => {
if (to.name === from.name) {
console.log("SAME ROUTE!!!");
}
});
</script>
Also try using useLazyAsyncData instead of useAsyncData and see if that changes anything
ap
apOP3mo ago
useLazyAsyncData did the trick ! Thanks for helping me through that !
brick
brick3mo ago
Great! Good luck with the rest of your project!

Did you find this page helpful?