| name | code-splitting |
| description | Safe code splitting with @suspensive/react lazy (experimental): .load() preloading, onSuccess/onError callbacks, createLazy for shared defaults, reloadOnError({ retry, retryDelay, storage, reload }) to recover from chunk-load failures caused by deploy version skew. Load when code-splitting routes/components, preloading on hover, or handling "Loading chunk failed" errors.
|
| 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/lazy.mdx","toss/suspensive:packages/react/src/lazy.ts"] |
Code splitting with lazy, createLazy, and reloadOnError
This skill builds on react. Read ../SKILL.md first for the client-only constraint and the .with() HOC pattern.
lazy, createLazy, and reloadOnError are experimental — the interface may change between minor versions. Pin the @suspensive/react version and re-check these APIs on upgrade.
Setup
'use client'
import { Suspense, lazy } from '@suspensive/react'
const Settings = lazy(() => import('./Settings'))
export const Example = () => (
<Suspense fallback={<div>Loading...</div>}>
<Settings />
</Suspense>
)
lazy is a drop-in for React.lazy that additionally exposes a static load(): Promise<void> method and accepts { onSuccess, onError } callbacks as a second argument.
Core Patterns
Preload a route component on hover
'use client'
import { Suspense, lazy } from '@suspensive/react'
import { useState } from 'react'
const Settings = lazy(() => import('./Settings'))
export const Example = () => {
const [open, setOpen] = useState(false)
return (
<div>
<button onMouseEnter={() => Settings.load()} onClick={() => setOpen(true)}>
Open settings
</button>
{open && (
<Suspense fallback={<div>Loading...</div>}>
<Settings />
</Suspense>
)}
</div>
)
}
Component.load() fetches the chunk without rendering, so the component mounts instantly when it is actually shown.
Auto-reload when a deploy invalidates old chunks
'use client'
import { lazy, reloadOnError } from '@suspensive/react'
const CheckoutPage = lazy(() => import('./CheckoutPage'), reloadOnError({ retry: 3, retryDelay: 1000 }))
reloadOnError returns a LazyOptions object whose onError reloads the page (default window.location.reload) up to retry times with retryDelay ms between attempts, tracking the count in storage (default window.sessionStorage) to avoid infinite reload loops; onSuccess clears the count.
Shared defaults for every lazy component via createLazy
'use client'
import { createLazy, reloadOnError } from '@suspensive/react'
const lazy = createLazy(reloadOnError({ retry: 1, retryDelay: 1000 }))
const Page = lazy(() => import('./Page'))
const Modal = lazy(() => import('./Modal'), {
onError: ({ error }) => console.error('Modal chunk failed:', error),
})
createLazy(defaultOptions) returns a lazy with baked-in defaults; per-component callbacks run first, then the defaults (individual onError → default onError).
Custom storage and reload function
'use client'
import { createLazy, reloadOnError } from '@suspensive/react'
const lazy = createLazy(
reloadOnError({
retry: 5,
retryDelay: 2000,
storage: {
getItem: (key) => localStorage.getItem(key),
setItem: (key, value) => localStorage.setItem(key, value),
removeItem: (key) => localStorage.removeItem(key),
},
reload: () => {
window.location.href = window.location.href
},
})
)
const Component = lazy(() => import('./Component'))
Common Mistakes
MEDIUM React.lazy with no chunk-failure recovery
Wrong:
import { lazy } from 'react'
const Page = lazy(() => import('./Page'))
Correct:
import { createLazy, reloadOnError } from '@suspensive/react'
const lazy = createLazy(reloadOnError({ retry: 1 }))
const Page = lazy(() => import('./Page'))
After a deploy, stale HTML requests old hashed chunks and React.lazy rejects forever until a manual refresh; reloadOnError retries then reloads, using storage to avoid reload loops.
Source: docs/suspensive.org/src/content/en/docs/react/lazy.mdx
MEDIUM Passing reloadOnError as the onError callback
Wrong:
import { createLazy, reloadOnError } from '@suspensive/react'
const lazy = createLazy({ onError: reloadOnError({ retry: 1 }) })
Correct:
import { createLazy, reloadOnError } from '@suspensive/react'
const lazy = createLazy(reloadOnError({ retry: 1 }))
reloadOnError(options) returns a whole LazyOptions object ({ onSuccess, onError }), not an onError callback — nesting it under onError never triggers a reload and silently drops the success-side count reset.
Source: packages/react/src/lazy.ts (reloadOnError returns LazyOptions)
MEDIUM Treating the lazy interface as stable
Wrong:
import { createLazy, reloadOnError } from '@suspensive/react'
export const appLazy = createLazy(reloadOnError({ retry: 3 }))
Correct:
import { createLazy, reloadOnError } from '@suspensive/react'
export const appLazy = createLazy(reloadOnError({ retry: 3 }))
lazy/createLazy/reloadOnError are marked experimental; the interface may change between minor versions, so version upgrades can break call sites without a major bump.
Source: docs/suspensive.org/src/content/en/docs/react/lazy.mdx (experimental callout)