@@ -106,7 +106,15 @@ const { data: readmeData } = useLazyFetch<ReadmeResponse>(
106106 const version = requestedVersion .value
107107 return version ? ` ${base }/v/${version } ` : base
108108 },
109- { default : () => ({ html: ' ' , mdExists: false , playgroundLinks: [], toc: [] }) },
109+ {
110+ default : () => ({
111+ html: ' ' ,
112+ mdExists: false ,
113+ playgroundLinks: [],
114+ toc: [],
115+ defaultValue: true ,
116+ }),
117+ },
110118)
111119
112120const playgroundLinks = computed (() => [
@@ -259,18 +267,19 @@ const nuxtApp = useNuxtApp()
259267const route = useRoute ()
260268const hasEmptyPayload =
261269 import .meta .client &&
262- nuxtApp .isHydrating &&
263270 nuxtApp .payload .serverRendered &&
264271 ! Object .keys (nuxtApp .payload .data ?? {}).length
265- const isSpaFallback = shallowRef (hasEmptyPayload && ! nuxtApp .payload .path )
272+ const isSpaFallback = shallowRef (nuxtApp . isHydrating && hasEmptyPayload && ! nuxtApp .payload .path )
266273const isHydratingWithServerContent = shallowRef (
267- hasEmptyPayload && nuxtApp .payload .path === route .path ,
274+ nuxtApp . isHydrating && hasEmptyPayload && nuxtApp .payload .path === route .path ,
268275)
276+ const hasServerContentOnly = shallowRef (hasEmptyPayload && nuxtApp .payload .path === route .path )
277+
269278// When we have server-rendered content but no payload data, capture the server
270279// DOM before Vue's hydration replaces it. This lets us show the server-rendered
271280// HTML as a static snapshot while data refetches, avoiding any visual flash.
272281const serverRenderedHtml = shallowRef <string | null >(
273- isHydratingWithServerContent .value
282+ hasServerContentOnly .value
274283 ? (document .getElementById (' package-article' )?.innerHTML ?? null )
275284 : null ,
276285)
@@ -279,7 +288,6 @@ if (isSpaFallback.value || isHydratingWithServerContent.value) {
279288 nuxtApp .hooks .hookOnce (' app:suspense:resolve' , () => {
280289 isSpaFallback .value = false
281290 isHydratingWithServerContent .value = false
282- serverRenderedHtml .value = null
283291 })
284292}
285293
@@ -733,27 +741,26 @@ const showSkeleton = shallowRef(false)
733741 <!-- Scenario 1: SPA fallback — show skeleton (no real content to preserve) -->
734742 <!-- Scenario 2: SSR with missing payload — preserve server DOM, skip skeleton -->
735743 <PackageSkeleton
736- v-if ="
737- isSpaFallback || (!isHydratingWithServerContent && (showSkeleton || status === 'pending'))
738- "
744+ v-if =" isSpaFallback || (!hasServerContentOnly && (showSkeleton || status === 'pending'))"
739745 />
740746
741747 <!-- During hydration without payload, show captured server HTML as a static snapshot.
742748 This avoids a visual flash: the user sees the server content while data refetches.
743749 v-html is safe here: the content originates from the server's own SSR output,
744- captured from the DOM before hydration — it is not user-controlled input. -->
750+ captured from the DOM before hydration — it is not user-controlled input.
751+ We also show SSR output until critical data is loaded, so that after rendering dynamic
752+ content, the user receives the same result as he received from the server-->
745753 <article
746- v-else-if =" isHydratingWithServerContent && serverRenderedHtml"
754+ v-else-if ="
755+ isHydratingWithServerContent ||
756+ (hasServerContentOnly && serverRenderedHtml && (!pkg || readmeData?.defaultValue))
757+ "
747758 id =" package-article"
748759 :class =" $style.packagePage"
749760 v-html =" serverRenderedHtml"
750761 />
751762
752- <article
753- v-else-if =" status === 'success' && pkg"
754- id =" package-article"
755- :class =" $style.packagePage"
756- >
763+ <article v-else-if =" pkg" id =" package-article" :class =" $style.packagePage" >
757764 <!-- Package header -->
758765 <header
759766 class =" sticky top-14 z-1 bg-[--bg] py-2 border-border"
0 commit comments