npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@niche-works/react-defer-rendering

v0.6.0

Published

`@niche-works/react-defer-rendering` is a niche library specialized in deferring the rendering of components until a specified condition is met.\ It provides a set of hooks covering a wide range of conditions, including timers, Promises, and browser APIs

Readme

@niche-works/react-defer-rendering

@niche-works/react-defer-rendering is a niche library specialized in deferring the rendering of components until a specified condition is met.
It provides a set of hooks covering a wide range of conditions, including timers, Promises, and browser APIs (matchMedia, IntersectionObserver, etc.).

日本語のREADMEはこちら

Installation

npm install @niche-works/react-defer-rendering
# or
pnpm add @niche-works/react-defer-rendering

Usage

Each hook returns a state ('pending' | 'fallback' | 'ready') representing the rendering status of the target node, along with the node that should be rendered for that state.

import { useDeferUntilTrue } from '@niche-works/react-defer-rendering';

const { node } = useDeferUntilTrue(<MyComponent />, isReady, {
  pending: <Spinner />,
});

return node;
  • While the condition is not met (pending), the node specified in the pending option is rendered
  • Once the condition is met (ready), rendering switches to the target node passed as the first argument
  • Some hooks also support a failure state (fallback)

When the target to wait for is not specified

Hooks that take something to wait for become ready immediately when the second argument is null / undefined, since there is nothing to wait for. If it later becomes unspecified, the state returns to ready; if it changes from unspecified to a value, the hook starts waiting again from pending.
Because hooks cannot be called conditionally, you can make deferred rendering an optional feature of your component by passing undefined as the second argument.

| Hook | Second argument treated as unspecified | | ---------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------- | | useDeferUntilTimeout, useDeferUntilDate, useDeferUntilResolved, useDeferUntilAsyncComplete | null / undefined | | useDeferUntilBreakpoint, useDeferUntilRender | null / undefined / empty string | | useDeferUntilWebFontReady | null / undefined / empty string / empty array | | useDeferUntilIntersected, useDeferUntilScrolled | The ref object itself is null / undefined |

For useDeferUntilIntersected and useDeferUntilScrolled, a ref whose current is null means the element has not been mounted yet, so the state stays pending.

function Heading({ fontFamily, children }: Props) {
  // Render immediately when fontFamily is not specified
  const { node } = useDeferUntilWebFontReady(<h1>{children}</h1>, fontFamily, {
    pending: null,
  });
  return node;
}

Note that the second argument of useDeferUntilTrue is the condition itself, so null / undefined is treated as unmet (pending).

Basic Hooks

useDeferUntilReady

The most fundamental hook, controlling rendering by directly specifying a state ('pending' | 'fallback' | 'ready'). All other hooks are built on top of this one.

import { useDeferUntilReady } from '@niche-works/react-defer-rendering';

const { node } = useDeferUntilReady(<MyComponent />, state, {
  pending: <Spinner />,
  fallback: <ErrorMessage />,
});

useDeferUntilTrue

Defers rendering until a boolean condition becomes true.

import { useDeferUntilTrue } from '@niche-works/react-defer-rendering';

const { node } = useDeferUntilTrue(<MyComponent />, isReady, {
  pending: <Spinner />,
});

Waiting for Elapsed Time

useDeferUntilTimeout

Defers rendering until the specified duration (in milliseconds) has elapsed.

import { useDeferUntilTimeout } from '@niche-works/react-defer-rendering';

const { node } = useDeferUntilTimeout(<MyComponent />, 3000, {
  pending: <Spinner />,
});

useDeferUntilDate

Defers rendering until the specified date and time.

import { useDeferUntilDate } from '@niche-works/react-defer-rendering';

const { node } = useDeferUntilDate(
  <Campaign />,
  new Date('2026-01-01T00:00:00'),
  { pending: <ComingSoon /> },
);

Waiting for Async Completion

useDeferUntilResolved

Defers rendering until a Promise settles (resolves or rejects).

import { useDeferUntilResolved } from '@niche-works/react-defer-rendering';

const { node } = useDeferUntilResolved(<MyComponent />, fetchPromise, {
  pending: <Spinner />,
  fallback: <ErrorMessage />,
});

useDeferUntilAsyncComplete

Defers rendering until an async function finishes executing. asyncFn is invoked after mount (inside an effect), which also manages the creation of the Promise itself. It is not invoked during SSR.\nSince asyncFn is re-invoked whenever its reference changes, memoize it with useCallback or similar.

import { useDeferUntilAsyncComplete } from '@niche-works/react-defer-rendering';
import { useCallback } from 'react';

const loadData = useCallback(
  () => fetch('/api/data').then((res) => res.json()),
  [],
);
const { node } = useDeferUntilAsyncComplete(<MyComponent />, loadData, {
  pending: <Spinner />,
});

Waiting for a Value to Change

useDeferUntilChange

Defers rendering until the specified value changes. Each time the value changes, it briefly passes through the pending state.

import { useDeferUntilChange } from '@niche-works/react-defer-rendering';

const { node } = useDeferUntilChange(<Toast>{message}</Toast>, message, {
  pending: null,
});

Manual Control at Any Timing

useDeferUntilOnReady

Lets you control the state at any timing by calling the returned onReady / onFallback / onPending handlers. Useful for conditions the hook itself cannot detect, such as event handlers.

import { useDeferUntilOnReady } from '@niche-works/react-defer-rendering';

const { node, onReady } = useDeferUntilOnReady(<Video />, {
  pending: <Spinner />,
});

<video onCanPlay={onReady}>{node}</video>;

useDeferUntilCallThreshold

Similar to useDeferUntilOnReady, but the state only switches once each handler has been called the specified number of times.

import { useDeferUntilCallThreshold } from '@niche-works/react-defer-rendering';

const { node, onReady } = useDeferUntilCallThreshold(<Gallery />, {
  pending: <Spinner />,
  onReadyCount: 3, // becomes ready once onReady has been called 3 times
});

Waiting for Browser State

Note (SSR / RSC): The hooks listed here depend on browser APIs, so their actual state cannot be determined on the server. See Using with SSR / RSC for details.

useDeferUntilBreakpoint

Defers rendering until a media query matches.

import { useDeferUntilBreakpoint } from '@niche-works/react-defer-rendering';

const { node } = useDeferUntilBreakpoint(
  <DesktopNav />,
  '(min-width: 1024px)',
  { pending: <MobileNav /> },
);

useDeferUntilIntersected

Defers rendering until a reference element enters the viewport.

import { useRef } from 'react';
import { useDeferUntilIntersected } from '@niche-works/react-defer-rendering';

const elementRef = useRef<HTMLDivElement>(null);
const { node } = useDeferUntilIntersected(<HeavyChart />, elementRef, {
  pending: <Placeholder />,
});

<div ref={elementRef}>{node}</div>;

useDeferUntilScrolled

Defers rendering until a reference element becomes visible as a result of scrolling.

const { node } = useDeferUntilScrolled(<LazyImage />, elementRef, {
  pending: <Placeholder />,
  rootMargin: 100,
});

useDeferUntilRender

Defers rendering until an element matching the selector is rendered into the DOM. Useful for waiting on elements outside of your own control, such as ones inserted by third-party scripts.

import { useDeferUntilRender } from '@niche-works/react-defer-rendering';

const { node } = useDeferUntilRender(<Overlay />, '#third-party-widget', {
  pending: null,
});

useDeferUntilWebFontReady

Defers rendering until all specified web fonts become available. Web fonts are checked with @niche-works/web-font-observer.
It targets web fonts defined with @font-face. System fonts cannot be detected as loaded, so specifying one results in fallback after the timeout.

import { useDeferUntilWebFontReady } from '@niche-works/react-defer-rendering';

const { node } = useDeferUntilWebFontReady(
  <Heading>Title</Heading>,
  'Noto Sans JP',
  {
    pending: <Heading style={{ visibility: 'hidden' }}>Title</Heading>,
  },
);

To wait for multiple web fonts, pass an array. To specify the weight and so on, pass an object. null / undefined in the array are ignored, so you can add web fonts conditionally.
Arrays and objects can be written inline (waiting does not restart unless their contents change).

const { node } = useDeferUntilWebFontReady(<Article />, [
  { family: 'Noto Sans JP', text: 'あ' },
  { family: 'Roboto', weight: 400 },
  isBold ? { family: 'Roboto', weight: 700 } : null,
]);

| Option | Type | Default | Description | | --------------- | ---------------------------------------- | ----------- | ---------------------------------------------------------------------------------------------------- | | timeout? | number | 3000 | Time in milliseconds before the state becomes fallback when a web font is not available | | loader? | (signal: AbortSignal) => Promise<void> | - | Function that loads the web fonts. Checking starts after it completes. Called only when the fonts change | | initialState? | 'pending' \| 'ready' \| 'fallback' | 'pending' | Initial state in environments where the loading state cannot be determined, such as SSR |

weight / style / width / text in the object form are the same as in @niche-works/web-font-observer.

  • If any web font does not become available, the state becomes fallback and the remaining waits are aborted.
  • Web fonts that are already loaded become ready without waiting (no flicker for cached fonts).
  • If a web font finishes loading after the state became fallback, the state becomes ready. Use preserveOnceFallback to keep fallback.
  • Place the @font-face definitions outside the deferred node (e.g. global CSS or a layout). If they are inside the deferred node, they are never registered and the hook waits until the timeout.

Common Options

Options for pending / ready

These options are shared across all hooks.

| Option | Type | Description | | -------------------- | ----------- | ---------------------------------------------- | | pending? | ReactNode | Node rendered while waiting for the condition | | pendingDefer? | number | Delay (in ms) before showing pending | | readyDefer? | number | Delay (in ms) before showing the target node | | preserveOnceReady? | boolean | Whether to keep the ready state once reached |

Options for fallback

useDeferUntilReady, useDeferUntilResolved, useDeferUntilAsyncComplete, useDeferUntilOnReady, useDeferUntilCallThreshold, and useDeferUntilWebFontReady also support a fallback state for failures.

| Option | Type | Description | | ----------------------- | ----------- | ------------------------------------------------- | | fallback? | ReactNode | Node rendered on failure | | fallbackDefer? | number | Delay (in ms) before showing fallback | | preserveOnceFallback? | boolean | Whether to keep the fallback state once reached |

Using with SSR / RSC

Every hook in this library ships with a 'use client' directive attached at build time. In React Server Components environments such as Next.js App Router, they are treated as client component boundaries without any additional configuration.

useDeferUntilBreakpoint, useDeferUntilIntersected, useDeferUntilScrolled, and useDeferUntilRender depend on browser APIs (matchMedia, IntersectionObserver, MutationObserver, etc.), so their actual state cannot be determined on the server. These hooks accept an initialCondition option to specify the value used during SSR (it is treated as false if not specified). Similarly, useDeferUntilWebFontReady accepts an initialState option to specify its initial state during SSR.

License

MIT