N
Nuxt5mo ago
mako

Breadcrumbs with async data (SSR)

Hi all, I'm currently facing a pretty straight forward problem, that I'm trying to solve for 2 days now. I have a PageHeader component that displays a breadcrumb. The PageHeader is using a pinia store to fetch the breadcrumb config and render it. In every page I populate the breadrcrumb store with the data that should be displayed (I'm fetching async data with pinia colada): pages/companies/[id].vue
<script setup lang="ts">
const route = useRoute();

const { getCompany } = useCompanies();
const { state } = getCompany(route.params.id as string);

const { setBreadcrumbs } = useBreadcrumbStore();
setBreadcrumbs([
{ label: 'Companies', to: '/companies' },
{ label: state.data.name || 'Company Details' },
]);
</script>
<script setup lang="ts">
const route = useRoute();

const { getCompany } = useCompanies();
const { state } = getCompany(route.params.id as string);

const { setBreadcrumbs } = useBreadcrumbStore();
setBreadcrumbs([
{ label: 'Companies', to: '/companies' },
{ label: state.data.name || 'Company Details' },
]);
</script>
When loading a page I'm getting hydration mismatch errors and the breadcrumb is initially empty and shows up only after a few milliseconds. So it flickers on page load. Also the async data in the breadcrumb is never shown as it is always undefined. On the page it shows without problems however. I found a few posts and issues now, dealing with the same kind of problems (e.g. https://github.com/nuxt/nuxt/discussions/19424) but it does not seem that there is a good solution to this problem. I also tried to solve it by using definePageMeta but it can't handle async data at all. So any ssr friendly way to implement breadcrumbs with dynamic data?
GitHub
SSR Question: How to pass data (e.g. breadcrumbs) from pages to com...
Hello all, I&#39;ve been dealing with a problem I just have for hours and have searched half the internet :) I would like to render a blog article on a page and put the breadcrumbs dynamically into...
5 Replies
kapa.ai
kapa.ai5mo ago
Hi @mako. I'm kapa.ai, your friendly support bot. I will try to answer your question by searching through the knowledge base. :wave: ---------------------- You're encountering a common SSR challenge in Nuxt: synchronizing async data (like company names) for breadcrumbs between server and client to avoid hydration mismatches and flickering. What's happening:
- When you fetch data asynchronously in a Pinia action and then set the breadcrumb, the breadcrumb state is only updated after the async fetch completes—often after the initial server render. - On the server, the breadcrumb is empty or incomplete, so the HTML sent to the client doesn't match what the client renders after hydration, causing a hydration mismatch and flicker. SSR-friendly solution:
To avoid this, you should fetch your async data using Nuxt's SSR-aware composables like useAsyncData or useFetch directly in your page component. These composables ensure that data is fetched on the server, included in the payload, and available immediately on the client—preventing double fetching and hydration issues. Recommended approach: 1. Fetch data with useAsyncData or useFetch in the page component. 2. Compute the breadcrumb synchronously from the fetched data. 3. Pass the breadcrumb as a prop to your PageHeader, or use a shared SSR-friendly state (like useState). Example:
<script setup lang="ts">
const route = useRoute()

// Fetch company data SSR-friendly
const { data: company } = await useAsyncData(
`company:${route.params.id}`,
() => $fetch(`/api/companies/${route.params.id}`)
)

// Compute breadcrumb synchronously from fetched data
const breadcrumbs = computed(() => [
{ label: 'Companies', to: '/companies' },
{ label: company.value?.name || 'Company Details' }
])
</script>

<template>
<PageHeader :breadcrumbs="breadcrumbs" />
</template>
<script setup lang="ts">
const route = useRoute()

// Fetch company data SSR-friendly
const { data: company } = await useAsyncData(
`company:${route.params.id}`,
() => $fetch(`/api/companies/${route.params.id}`)
)

// Compute breadcrumb synchronously from fetched data
const breadcrumbs = computed(() => [
{ label: 'Companies', to: '/companies' },
{ label: company.value?.name || 'Company Details' }
])
</script>

<template>
<PageHeader :breadcrumbs="breadcrumbs" />
</template>
Why not use Pinia for SSR-fetched data?
Pinia stores are not automatically SSR-hydrated with async data unless you use Nuxt's SSR-aware data fetching in the page and then initialize the store with that data. If you fetch data only in a Pinia action, it will not be available during SSR, leading to the issues you described. Nuxt recommends using useAsyncData or useFetch for SSR data fetching, and only using Pinia for client-side state or as a cache for already-fetched data Nuxt State Management Docs. References:
- SSR-friendly data fetching with useAsyncData - State Management best practices - Data Fetching in Nuxt 3 Summary:
- Fetch async data for breadcrumbs with useAsyncData/useFetch in the page. - Compute breadcrumbs synchronously from that data. - Pass breadcrumbs as props or use useState for SSR-friendly sharing. - Avoid relying on Pinia for SSR-fetched data unless you hydrate it manually. This approach will eliminate hydration mismatches and flickering for your breadcrumbs. ---------------------- Note: you can ask me a follow up question by @ mentioning me again :speech_balloon: ----------------------
kapa.ai
kapa.ai5mo ago
<@303246850591555585> Kapa.ai is still learning and improving, please let me know how I did by reacting below.
mako
makoOP5mo ago
@kapa.ai company data and breadcrumb config are not on the same page / same component with the PageHeader. So your proprsed solution will not work. The breadcrumb config is defined in a page and the PageHeader is part of the layout
kapa.ai
kapa.ai5mo ago
Let me check the knowledge base for you again. One moment! :books: ---------------------- You're correct—when the breadcrumb config is set in the page, but the PageHeader (which displays the breadcrumb) is in the layout, you run into a classic SSR timing issue: the layout (and its components) render before the page's async data is available. This means the layout cannot access the async-fetched breadcrumb data during the initial server render, leading to hydration mismatches and flicker. This is a known limitation in Nuxt's SSR lifecycle: layouts are rendered before the page's async data is resolved, so any data fetched in the page is not available to the layout or its components during SSR. This is confirmed in the discussion about Nuxt/Vue lifecycle data load mismatch, where it's explained that the layout is rendered before the page's data is fetched, causing the header (in the layout) to not have access to the data fetched in the page during SSR. As a result, the header only updates after hydration on the client, leading to flicker and mismatches (Nuxt/Vue lifecycle data load mismatch). There is no built-in, SSR-friendly way in Nuxt to pass async data from a page to a layout during the initial server render. The only reliable SSR-friendly solutions are: - Fetch the required data in the layout or a plugin/middleware (so it's available before the layout renders). - Move the PageHeader into the page (so it can access the page's async data directly). If you cannot fetch the company data or breadcrumb config in the layout or middleware, and you cannot move the PageHeader into the page, then unfortunately, there is no SSR-friendly way to achieve what you want with the current Nuxt lifecycle. Any solution that relies on setting state in the page and reading it in the layout will result in hydration mismatches and flicker. Summary:
- The knowledge sources confirm this is a limitation of the Nuxt SSR lifecycle. - There is no SSR-friendly way to pass async data from a page to a layout during initial render. - The only workarounds are to fetch the data in the layout/middleware, or move the header into the page. I'm sorry, but the knowledge sources do not provide a workaround that would allow you to keep your current structure without hydration issues. This is a known limitation in Nuxt's SSR data flow between pages and layouts (Nuxt/Vue lifecycle data load mismatch). ---------------------- Note: you can ask me a follow up question by @ mentioning me again :speech_balloon: ----------------------
kapa.ai
kapa.ai5mo ago
<@303246850591555585> Kapa.ai is still learning and improving, please let me know how I did by reacting below.

Did you find this page helpful?