Configuring client:only vs client:visible in Astro
Selecting the correct hydration directive in Astro is not a stylistic choice; it is a streaming boundary and main-thread scheduling decision. Misalignment between viewport priority, directive execution models, and SSR chunk allocation directly degrades Core Web Vitals, particularly Time to Interactive (TTI) and Interaction to Next Paint (INP). This diagnostic workflow maps hydration triggers to runtime constraints, providing measurable resolution strategies for production deployments.
Directive Execution Models & Streaming Boundaries
Astro’s streaming SSR architecture delivers HTML in discrete chunks, allowing the browser to parse and render critical content before non-critical payloads resolve. Hydration directives dictate when and how JavaScript payloads attach to these streamed islands.
client:only: Completely bypasses server-side rendering for the component. Astro outputs a lightweight placeholder during SSR, but the framework-specific JS bundle is requested immediately upon client-side hydration initialization. This eliminates SSR CPU overhead but forces synchronous or high-priority network requests for JS, which can block the streaming waterfall if placed above the Largest Contentful Paint (LCP) element.client:visible: Renders full HTML during SSR and streams it immediately. Hydration is deferred until anIntersectionObserverdetects the element entering the viewport. This aligns JS execution with browser idle time, preserving streaming throughput and reducing main-thread contention during initial load.
For baseline syntax and lifecycle mapping, consult the Astro Islands and Client Directives reference. The critical distinction lies in payload timing: client:only shifts JS fetch/parse to the critical path, while client:visible defers it to idle scheduling.
---
import InteractiveChart from '../components/InteractiveChart.astro';
---
Root-Cause Analysis: Misconfigured Directives
Performance degradation in Astro islands typically stems from directive-viewport misalignment. Use the following diagnostic matrix to map observed metrics to directive misconfiguration:
| Symptom | Primary Metric Impact | Likely Directive Misconfiguration | Resolution Path |
|---|---|---|---|
| High TTI + Blocked Streaming | TTI > 3.8s, LCP delayed | client:only applied to above-the-fold interactive elements |
Swap to client:visible or move below fold. Defer non-critical JS. |
| Unresponsive UI on Load | INP > 200ms, missing listeners | client:visible applied to modals, auth forms, or sticky headers |
Replace with client:load or client:only for immediate event binding. |
| CLS Spikes (>0.1) | CLS > 0.1, layout shift | Hydration mismatch from deferred layout calculation in client:visible |
Reserve static dimensions via CSS aspect-ratio or explicit min-height. |
| Sequential JS Waterfall | Network: High latency, low concurrency | client:only placed outside Suspense boundaries, forcing sequential chunk requests |
Wrap in <Suspense> or restructure route layout to parallelize streaming. |
Cross-reference these patterns with the broader Framework-Specific Islands & Streaming SSR architecture guide to validate adjacent debugging steps applicable to Next.js, SvelteKit, and Qwik implementations.
Diagnostic Workflows & Profiling
Isolate hydration bottlenecks using deterministic DevTools traces and Astro runtime markers. Execute the following workflow in a production-like environment (astro build && astro preview).
- Record Performance Trace: Open Chrome DevTools → Performance tab → Clear → Record → Reload page. Stop recording after
loadevent. - Filter Astro Markers: In the trace search bar, filter by
astro:island,astro:hydrate,astro:visible, andastro:ready. These markers delineate SSR chunk delivery, IO trigger, and hydration completion. - Measure Hydration Delta: Calculate
astro:readytimestamp minusastro:visibletimestamp. A delta > 150ms indicates heavy JS parse/compile overhead or main-thread contention. - Inspect Network Waterfall: Switch to Network tab → Filter by
JS. Verify chunk ordering.client:onlycomponents should appear asHighpriority requests; if they blockdocumentorLCPimage requests, streaming is disrupted. - Validate IntersectionObserver Thresholds: In the Console, run:
performance.getEntriesByType('mark').filter(m => m.name.includes('astro:visible'))
Cross-reference trigger coordinates with actual scroll behavior. Misaligned thresholds cause premature or delayed hydration.
6. Simulate Off-Screen Hydration: Use astro:prefetch in development to mock viewport entry and measure hydration cost without scroll interaction:
npx lighthouse http://localhost:4321 --view --only-categories=performance --preset=desktop
Precise Optimization Steps
Align directive selection with viewport priority and streaming architecture using build-time logic and explicit observer configuration.
1. Build-Time Conditional Assignment
Dynamically assign directives based on route metadata or component priority props. This prevents manual directive drift across large codebases.
---
const isAboveFold = Astro.props.priority === 'high';
---
{isAboveFold ? (
) : (
)}
2. IntersectionObserver Configuration Override
Default client:visible triggers at 0 threshold, which can cause hydration jank exactly at the viewport edge. Pre-empt hydration by configuring rootMargin and threshold to trigger slightly before visibility.
3. Progressive Enhancement & Fallback Strategy
Legacy browsers lacking native IntersectionObserver support will silently fail hydration. Implement a runtime check or polyfill injection in your layout:
if (!('IntersectionObserver' in window)) {
// Dynamically import polyfill or fallback to client:load
import('intersection-observer/polyfill');
}
4. Metric Verification & CI Integration
Validate optimizations against measurable targets:
- LCP: Maintain
< 2.5sby ensuringclient:onlynever blocks critical image/font requests. - INP: Target
< 200msby verifyingastro:readycompletes within 50ms of user interaction. - CLS: Keep
< 0.1by reserving island dimensions in CSS before hydration. - CI Enforcement: Integrate
@astrojs/lighthouseorweb-vitalsinto your pipeline to fail builds if directive misconfiguration regresses streaming throughput.
By strictly mapping hydration directives to viewport priority and streaming boundaries, you eliminate main-thread contention, preserve SSR chunk delivery, and maintain deterministic interactivity across all framework islands.