LazyMount
SM, under 950B gzippedDefers mounting expensive children until visible, sooner on good connections.
- Category
- Media & layout
- Budget
- sm, single-purpose control
- Runtime deps
- None
Preview
Mounted content
This is the exact fixture the conformance suite renders in CI. Switch the language to see the component mirror and reformat.
Install
npx shadcn@latest add https://kata-ui-rho.vercel.app/r/lazy-mount.jsonCopies the source into your project. Pulls in 1 primitive: use-network.
Source
"use client";
import { useEffect, useRef, useState } from "react";
import { useNetwork } from "../lib/use-network";
export interface LazyMountProps {
children: React.ReactNode;
fallback?: React.ReactNode;
/** Mount immediately regardless of visibility or connection. */
eager?: boolean;
}
/**
* Defers mounting expensive children (a video embed, a map, a chart library)
* until they scroll into view — and, on a constrained connection, until the
* user has already shown intent to be on this part of the page, rather than
* paying for it the moment it exists in the DOM.
*/
export function LazyMount({ children, fallback = null, eager = false }: LazyMountProps) {
const [visible, setVisible] = useState(eager);
const ref = useRef<HTMLDivElement>(null);
const { constrained } = useNetwork();
useEffect(() => {
if (eager || visible || !ref.current) return;
const observer = new IntersectionObserver(
(entries) => {
if (entries[0].isIntersecting) setVisible(true);
},
{ rootMargin: constrained ? "0px" : "200px" },
);
observer.observe(ref.current);
return () => observer.disconnect();
}, [eager, visible, constrained]);
return <div ref={ref}>{visible ? children : fallback}</div>;
}
What CI checks
- Bundled, minified and gzipped against its tier budget
- Audited by axe in the state previewed above
- Rendered through react-dom/server with no browser globals
- Scanned for network calls, dangerous sinks, and unguarded animation