Skip to main content

loading-ux

Loading UX without flash-of-loading in @suspensive/react: Suspense fallback, Delay (ms, fallback, isDelayed render prop) to hold back spinners and fade skeletons in, DefaultPropsProvider + new DefaultProps for app-wide default fallbacks. Load when writing Suspense fallbacks, skeletons, spinners, or global loading defaults.

Quellinformationen

Repository
toss/suspensive
Letzte Quellaktivität
4. Juli 2026 um 07:04
Erkannte Sprache von SKILL.md
Englisch
Sterne
1.059
Forks
99

Installationsoptionen

Standardmäßig ist der Prompt ausgewählt, der zuerst die Quelle prüft. Sie können zu einem direkten Befehl wechseln oder eine lokale Kopie herunterladen.

Quelldateien prüfen

Lesen Sie SKILL.md und alle von SkillsMP angezeigten Begleitdateien, bevor Sie sich für eine Installation entscheiden.

SKILL.md wird angezeigt

SKILL.md
Quellanweisungen · Schreibgeschützte Vorschau
name
loading-ux
description
Loading UX without flash-of-loading in @suspensive/react: Suspense fallback, Delay (ms, fallback, isDelayed render prop) to hold back spinners and fade skeletons in, DefaultPropsProvider + new DefaultProps for app-wide default fallbacks. Load when writing Suspense fallbacks, skeletons, spinners, or global loading defaults.
metadata
{"type":"sub-skill","library":"@suspensive/react","library_version":"3.21.2","framework":"react"}
requires
["react"]
sources
["toss/suspensive:docs/suspensive.org/src/content/en/docs/react/Suspense.mdx","toss/suspensive:docs/suspensive.org/src/content/en/docs/react/Delay.mdx","toss/suspensive:docs/suspensive.org/src/content/en/docs/react/DefaultPropsProvider.mdx","toss/suspensive:packages/react/src/Delay.tsx"]
# Loading UX with Suspense, Delay, and DefaultPropsProvider This skill builds on react. Read ../SKILL.md first for the client-only constraint and the `.with()` HOC pattern. ## Setup ```tsx 'use client' import { Delay, Suspense } from '@suspensive/react' import { Content } from './Content' import { Spinner } from './Spinner' export const Example = () => ( <Suspense fallback={ <Delay ms={200}> <Spinner /> </Delay> } > <Content /> </Suspense> ) ``` `Delay` postpones exposing its children by `ms` — if `Content` resolves within 200ms, the user never sees the spinner. Recommended `ms`: 100–300 for most UI, 500+ for non-critical sections. `DelayProps` is a union: either `{ ms?, fallback?: ReactNode, children?: ReactNode }` or `{ ms?, children?: ({ isDelayed }) => ReactNode }` (no `fallback` allowed with a render-prop child). ## Core Patterns ### Fade the skeleton in with the isDelayed render prop ```tsx 'use client' import { Delay, Suspense } from '@suspensive/react' import { Content } from './Content' import { Skeleton } from './Skeleton' export const Example = () => ( <Suspense fallback={ <Delay ms={200}> {({ isDelayed }) => <Skeleton style={{ opacity: isDelayed ? 1 : 0, transition: 'opacity 200ms' }} />} </Delay> } > <Content /> </Suspense> ) ``` When `children` is a render prop, `Delay` always renders it and flips `isDelayed` to `true` after `ms`, enabling opacity transitions instead of an abrupt pop-in (available since v3.12). ### Show an interim fallback before the delayed children ```tsx 'use client' import { Delay } from '@suspensive/react' export const Example = () => ( <Delay ms={200} fallback={<>Almost there...</>}> 200ms delayed content </Delay> ) ``` ### One default fallback for every Suspense and Delay in the app ```tsx 'use client' import { DefaultProps, DefaultPropsProvider, Suspense } from '@suspensive/react' import { Page } from './Page' import { Spinner } from './Spinner' const defaultProps = new DefaultProps({ Delay: { ms: 1200 }, Suspense: { fallback: <Spinner />, clientOnly: false }, }) export const App = () => ( <DefaultPropsProvider defaultProps={defaultProps}> <Suspense> <Page /> </Suspense> </DefaultPropsProvider> ) ``` `<Suspense>` and `<Delay>` without explicit props read defaults from the nearest provider; per-component props always win. ## Common Mistakes ### MEDIUM Hand-rolling spinner delay with useState/useEffect/setTimeout Wrong: ```tsx import { useEffect, useState } from 'react' const DelayedSpinner = () => { const [show, setShow] = useState(false) useEffect(() => { const t = setTimeout(() => setShow(true), 200) return () => clearTimeout(t) }, []) return show ? <Spinner /> : null } ``` Correct: ```tsx import { Delay, Suspense } from '@suspensive/react' const Example = ({ children }) => ( <Suspense fallback={ <Delay ms={200}> <Spinner /> </Delay> } > {children} </Suspense> ) ``` `Delay` does the timeout declaratively, composes inside any Suspense fallback, and picks up default `ms` from `DefaultPropsProvider`. Source: docs/suspensive.org/src/content/en/docs/react/Delay.mdx ### MEDIUM Passing a plain object to DefaultPropsProvider Wrong: ```tsx import { DefaultPropsProvider } from '@suspensive/react' const App = ({ children }) => ( <DefaultPropsProvider defaultProps={{ Suspense: { fallback: <Spinner /> } }}>{children}</DefaultPropsProvider> ) ``` Correct: ```tsx import { DefaultProps, DefaultPropsProvider } from '@suspensive/react' const defaultProps = new DefaultProps({ Suspense: { fallback: <Spinner /> } }) const App = ({ children }) => <DefaultPropsProvider defaultProps={defaultProps}>{children}</DefaultPropsProvider> ``` `defaultProps` must be an instance of the `DefaultProps` class; a plain object literal is not accepted. Source: docs/suspensive.org/src/content/en/docs/react/DefaultPropsProvider.mdx ### MEDIUM Abrupt skeleton-to-content swap instead of isDelayed fade Wrong: ```tsx import { Delay } from '@suspensive/react' // plus a separate hand-written fade-in component around Spinner const Fallback = () => ( <Delay ms={200}> <FadeIn> <Spinner /> </FadeIn> </Delay> ) ``` Correct: ```tsx import { Delay } from '@suspensive/react' const Fallback = () => ( <Delay ms={200}> {({ isDelayed }) => <Spinner style={{ opacity: isDelayed ? 1 : 0, transition: 'opacity 200ms' }} />} </Delay> ) ``` The render-prop child receives `isDelayed` (v3.12+) exactly for this; agents unaware of it wire CSS animation components by hand. Source: docs/suspensive.org/src/content/en/docs/react/Delay.mdx (#1312) ### MEDIUM Reading Delay ms as extending fallback display time Wrong: ```tsx import { Delay, Suspense } from '@suspensive/react' // expecting the spinner to stay visible for at least 3s const Example = ({ children }) => ( <Suspense fallback={ <Delay ms={3000}> <Spinner /> </Delay> } > {children} </Suspense> ) ``` Correct: ```tsx import { Delay, Suspense } from '@suspensive/react' // ms only postpones when the spinner APPEARS; content replaces it as soon as it resolves const Example = ({ children }) => ( <Suspense fallback={ <Delay ms={200}> <Spinner /> </Delay> } > {children} </Suspense> ) ``` `Delay` postpones exposing its children/fallback; it never prolongs how long a fallback stays visible — a 1s fetch with `ms={3000}` shows no spinner at all, not 4s of spinner. Source: https://github.com/toss/suspensive/issues/182 See also: the `declarative-queries` skill in `@suspensive/react-query-4`/`@suspensive/react-query-5` — `Delay` composes inside Suspense fallbacks that wrap `SuspenseQuery`.
Auf GitHub ansehen