Fix: Multiple Hydration Strategies Not Supported in Nuxt

Error message:
Multiple hydration strategies on a single component are not yet supported.
ssr 2025-01-25

What Causes This Error?

This error occurs when you try to apply multiple hydration directives to the same component. Nuxt components can only have one hydration strategy at a time.

The Problem

<template>
  <!-- ❌ Wrong - multiple hydration strategies -->
  <LazyMyComponent
    lazy-hydrate-on-visible
    lazy-hydrate-on-idle
  />

  <!-- ❌ Wrong - combining strategies -->
  <LazyChart
    :lazy-hydrate-on-visible="true"
    :lazy-hydrate-on-interaction="['click']"
  />
</template>

The Fix

Choose One Strategy

<template>
  <!-- ✅ Correct - single strategy -->
  <LazyMyComponent lazy-hydrate-on-visible />

  <!-- OR -->
  <LazyMyComponent lazy-hydrate-on-idle />

  <!-- OR -->
  <LazyMyComponent :lazy-hydrate-on-interaction="['click', 'mouseover']" />
</template>

Understanding Hydration Strategies

Available Strategies

StrategyWhen It Hydrates
lazy-hydrate-on-visibleComponent enters viewport
lazy-hydrate-on-idleBrowser is idle
lazy-hydrate-on-interactionUser interacts (click, hover, etc.)
lazy-hydrate-neverNever hydrates (static only)

On Visible

<template>
  <!-- Hydrates when scrolled into view -->
  <LazyHeavyChart lazy-hydrate-on-visible />

  <!-- With options -->
  <LazyHeavyChart
    lazy-hydrate-on-visible
    :lazy-hydrate-options="{ rootMargin: '100px' }"
  />
</template>

On Idle

<template>
  <!-- Hydrates when browser is idle -->
  <LazyAnalytics lazy-hydrate-on-idle />

  <!-- With timeout -->
  <LazyAnalytics
    lazy-hydrate-on-idle
    :lazy-hydrate-options="{ timeout: 5000 }"
  />
</template>

On Interaction

<template>
  <!-- Hydrates on any interaction -->
  <LazyDropdown :lazy-hydrate-on-interaction="true" />

  <!-- Hydrates on specific events -->
  <LazyDropdown :lazy-hydrate-on-interaction="['click', 'focus']" />

  <!-- Hydrates on mouseover -->
  <LazyTooltip :lazy-hydrate-on-interaction="['mouseover']" />
</template>

Never Hydrate

<template>
  <!-- Static content, never hydrates -->
  <LazyStaticBanner lazy-hydrate-never />
</template>

Choosing the Right Strategy

Heavy Components Below Fold

<template>
  <HeroSection />  <!-- Immediate hydration -->

  <!-- Below fold - hydrate when visible -->
  <LazyTestimonials lazy-hydrate-on-visible />
  <LazyFooter lazy-hydrate-on-visible />
</template>

Analytics/Tracking

<template>
  <!-- Non-critical, hydrate when idle -->
  <LazyAnalyticsWidget lazy-hydrate-on-idle />
  <LazyChatWidget lazy-hydrate-on-idle />
</template>

Interactive Elements

<template>
  <!-- Needs interaction to work -->
  <LazyModal :lazy-hydrate-on-interaction="['click']" />
  <LazyAccordion :lazy-hydrate-on-interaction="['click']" />
</template>

Static Content

<template>
  <!-- Never needs JS -->
  <LazyLegalFooter lazy-hydrate-never />
  <LazyStaticSidebar lazy-hydrate-never />
</template>

Alternative: Wrapper Component

If you need complex hydration logic, create a wrapper:

<!-- components/SmartHydrate.vue -->
<script setup>
const props = defineProps<{
  priority: 'high' | 'medium' | 'low' | 'none'
}>()
</script>

<template>
  <!-- High priority - immediate -->
  <slot v-if="priority === 'high'" />

  <!-- Medium priority - on visible -->
  <ClientOnly v-else-if="priority === 'medium'">
    <slot />
  </ClientOnly>

  <!-- Low priority - on idle -->
  <LazyClientOnly v-else-if="priority === 'low'" lazy-hydrate-on-idle>
    <slot />
  </LazyClientOnly>

  <!-- None - never hydrate -->
  <div v-else>
    <slot />
  </div>
</template>

Usage:

<template>
  <SmartHydrate priority="medium">
    <MyComponent />
  </SmartHydrate>
</template>

Combining with Lazy Loading

<template>
  <!-- Lazy (code-split) + hydration strategy -->
  <LazyExpensiveComponent lazy-hydrate-on-visible />

  <!-- vs immediate load but delayed hydration -->
  <ExpensiveComponent lazy-hydrate-on-idle />
</template>

Configuration

Enable delayed hydration in config:

// nuxt.config.ts
export default defineNuxtConfig({
  experimental: {
    // Required for hydration strategies
    componentIslands: true
  }
})

Debugging

Check hydration status:

<script setup>
// See when component hydrates
onMounted(() => {
  console.log('Component hydrated at:', performance.now())
})
</script>

Quick Checklist

  • Only one hydration strategy per component
  • Choose strategy based on component priority
  • Use lazy-hydrate-on-visible for below-fold content
  • Use lazy-hydrate-on-idle for non-critical features
  • Use lazy-hydrate-on-interaction for interactive elements
  • Use lazy-hydrate-never for static content
  • Enable experimental.componentIslands if needed