fix(use-image): resolve race condition with cached images on Firefox/Safari#6041
Conversation
…Safari The bug occurred because event handlers (onload/onerror) were attached AFTER setting the image src. For cached images, browsers fire onload synchronously when src is set, causing the event to be missed and images to remain stuck at opacity-0. Changes: - Attach handlers BEFORE setting src to catch synchronous callbacks - Check both naturalWidth AND naturalHeight (per CodeRabbit review on #4523) - Handle synchronous error callbacks for failed cached images - Add comprehensive test coverage (15 tests) including: - Synchronous cached image success (Firefox/Safari race condition) - Synchronous cached image error - Dynamic src changes - All props (crossOrigin, srcSet, sizes, loading) - Callback invocation verification Reproduction and investigation performed using Claude Opus 4.5. Fixes #4534, #2259
🦋 Changeset detectedLatest commit: e7b5fe1 The changes in this PR will be included in the next version bump. This PR includes changesets to release 5 packages
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
|
@brianathere is attempting to deploy a commit to the HeroUI Inc Team on Vercel. A member of the Team first needs to authorize it. |
WalkthroughFixes a race condition in the Changes
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Pre-merge checks and finishing touches✅ Passed checks (5 passed)
✨ Finishing touches
🧪 Generate unit tests (beta)
📜 Recent review detailsConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro 📒 Files selected for processing (3)
🚧 Files skipped from review as they are similar to previous changes (1)
🧰 Additional context used🧠 Learnings (3)📚 Learning: 2025-10-27T21:52:33.324ZApplied to files:
📚 Learning: 2025-10-25T17:11:59.338ZApplied to files:
📚 Learning: 2025-10-27T21:48:35.308ZApplied to files:
🔇 Additional comments (8)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (1)
packages/hooks/use-image/__tests__/use-image.test.tsx (1)
220-362: Comprehensive property propagation tests.The custom mocks with getter/setter pairs correctly capture property assignments, verifying that
crossOrigin,srcSet,sizes, andloadingare properly set on the image element beforesrcis assigned.Minor: The
asynckeyword on these tests (lines 221, 263, 321) is unnecessary since they don't useawait. Consider removing them for consistency:- it("sets crossOrigin property on image element", async () => { + it("sets crossOrigin property on image element", () => {
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (3)
.changeset/fix-use-image-race-condition.mdpackages/hooks/use-image/__tests__/use-image.test.tsxpackages/hooks/use-image/src/index.ts
🧰 Additional context used
🧠 Learnings (3)
📚 Learning: 2025-10-27T21:52:33.324Z
Learnt from: adbjo
Repo: heroui-inc/heroui PR: 5846
File: packages/components/tabs/src/tabs.tsx:115-125
Timestamp: 2025-10-27T21:52:33.324Z
Learning: In packages/components/tabs/src/tabs.tsx, the useEffect dependency array at line 125 intentionally uses `domRef.current` rather than `domRef` because domRef.current can change between renders (when React sets it during the commit phase), whereas domRef itself has stable identity and won't change.
Applied to files:
packages/hooks/use-image/src/index.ts
📚 Learning: 2025-10-25T17:11:59.338Z
Learnt from: adbjo
Repo: heroui-inc/heroui PR: 5846
File: packages/components/tabs/src/tabs.tsx:155-155
Timestamp: 2025-10-25T17:11:59.338Z
Learning: In packages/components/tabs/src/tabs.tsx, the renderTabs useMemo dependency array intentionally includes both `domRef` and `cursorRef` to maintain consistency in how ref objects are handled in dependency arrays, even though ref objects have stable identity across renders.
Applied to files:
packages/hooks/use-image/src/index.ts
📚 Learning: 2025-10-27T21:48:35.308Z
Learnt from: adbjo
Repo: heroui-inc/heroui PR: 5846
File: packages/components/tabs/src/tabs.tsx:76-101
Timestamp: 2025-10-27T21:48:35.308Z
Learning: In packages/components/tabs/src/tabs.tsx, the updateCursorPosition useCallback dependency array intentionally includes `cursorRef.current` to handle the case where the cursor span element is unmounted and remounted (e.g., when `disableAnimation` or `disableCursorAnimation` toggles). This ensures the callback is recreated when the ref points to a new element, triggering a dependency chain that re-establishes the ResizeObserver and initializes the new cursor element with the data-initialized attribute.
</learning]
Applied to files:
packages/hooks/use-image/src/index.ts
🧬 Code graph analysis (1)
packages/hooks/use-image/__tests__/use-image.test.tsx (3)
packages/hooks/use-image/src/index.ts (1)
useImage(75-160)packages/utilities/test-utils/src/mocks/image.ts (1)
mockImage(5-48)packages/utilities/test-utils/src/mocks/index.ts (1)
mocks(3-5)
🔇 Additional comments (9)
.changeset/fix-use-image-race-condition.md (1)
1-18: Well-documented changeset with clear root cause analysis.The changeset provides excellent documentation of the bug fix, including the root cause, solution, and references to the relevant issues. The patch version bumps are appropriate for this bug fix.
packages/hooks/use-image/src/index.ts (3)
94-100: Clean implementation of the flush function.The flush function correctly clears event handlers before nulling the ref, preventing potential memory leaks and stale callbacks. The empty dependency array is appropriate since
imageRefis a stable ref object.
111-131: Core race condition fix is correctly implemented.The handler attachment order (handlers → properties → src) properly addresses the race condition. Calling
flush()within handlers ensures cleanup happens regardless of sync/async callback timing.
136-144: Proper fallback handling for already-complete cached images.The check for both
naturalWidthandnaturalHeightcorrectly handles the case where cached images may already be complete whensrcis assigned. This aligns with the HTML specification for detecting successfully loaded images.Note: If the synchronous
onloadhandler fires before this check, both the handler'ssetStatus("loaded")and the returned"loaded"status will trigger state updates, but React will deduplicate identical state updates, so this is harmless.packages/hooks/use-image/__tests__/use-image.test.tsx (5)
16-38: Solid coverage of basic loading state transitions.The tests correctly validate the pending → loading → loaded/failed state machine. The async patterns with
waitForare appropriate for testing the hook's asynchronous behavior.
48-95: Excellent test for the Firefox/Safari race condition.This test directly validates the core fix by simulating synchronous
onloadfiring whensrcis set. The custom mock with getter/setter forsrcaccurately replicates cached image behavior. Good use of try/finally for cleanup.
141-182: Good coverage of dynamic src change scenarios.The tests properly validate state transitions when
srcchanges. The mock reset at lines 170-171 correctly simulates a fresh image load for the new URL.
184-198: Correct validation of bypass options.Both
ignoreFallbackandshouldBypassImageLoadcorrectly short-circuit to return"loaded"immediately, and the tests validate this synchronous behavior.
200-218: Proper validation of callback invocations.The tests verify that
onLoadandonErrorcallbacks are invoked at the appropriate times during the image loading lifecycle.
Address CodeRabbit review feedback: the `ignoreFallback` prop was used inside the `load` callback (line 104) but was missing from the dependency array, creating a stale closure bug. Without this fix, if `ignoreFallback` changes from `true` to `false` dynamically, the `load` callback would retain the stale `true` value, preventing the image from ever loading. Changes: - Add `ignoreFallback` to useCallback dependency array - Add tests for dynamic `ignoreFallback` changes (both directions) - Update changeset to document this fix Verification performed using Claude Opus 4.5.
@heroui/accordion
@heroui/alert
@heroui/autocomplete
@heroui/avatar
@heroui/badge
@heroui/breadcrumbs
@heroui/button
@heroui/calendar
@heroui/card
@heroui/checkbox
@heroui/chip
@heroui/code
@heroui/date-input
@heroui/date-picker
@heroui/divider
@heroui/drawer
@heroui/dropdown
@heroui/form
@heroui/image
@heroui/input
@heroui/input-otp
@heroui/kbd
@heroui/link
@heroui/listbox
@heroui/menu
@heroui/modal
@heroui/navbar
@heroui/number-input
@heroui/pagination
@heroui/popover
@heroui/progress
@heroui/radio
@heroui/ripple
@heroui/scroll-shadow
@heroui/select
@heroui/skeleton
@heroui/slider
@heroui/snippet
@heroui/spacer
@heroui/spinner
@heroui/switch
@heroui/table
@heroui/tabs
@heroui/toast
@heroui/tooltip
@heroui/user
@heroui/react
@heroui/system
@heroui/system-rsc
@heroui/theme
@heroui/use-aria-accordion
@heroui/use-aria-accordion-item
@heroui/use-aria-button
@heroui/use-aria-link
@heroui/use-aria-modal-overlay
@heroui/use-aria-multiselect
@heroui/use-aria-overlay
@heroui/use-callback-ref
@heroui/use-clipboard
@heroui/use-data-scroll-overflow
@heroui/use-disclosure
@heroui/use-draggable
@heroui/use-form-reset
@heroui/use-image
@heroui/use-infinite-scroll
@heroui/use-intersection-observer
@heroui/use-is-mobile
@heroui/use-is-mounted
@heroui/use-measure
@heroui/use-pagination
@heroui/use-real-shape
@heroui/use-ref-state
@heroui/use-resize
@heroui/use-safe-layout-effect
@heroui/use-scroll-position
@heroui/use-ssr
@heroui/use-theme
@heroui/use-update-effect
@heroui/use-viewport-size
@heroui/aria-utils
@heroui/dom-animation
@heroui/framer-utils
@heroui/react-rsc-utils
@heroui/react-utils
@heroui/shared-icons
@heroui/shared-utils
@heroui/stories-utils
@heroui/test-utils
commit: |
* fix(theme): hide password reveal button (#5990) * fix(link): link overriding role (#5999) * fix(link): allow overriding role * chore(link): changeset * chore(deps): upgrade react-aria (v1.14.0) (#5996) * chore(deps): upgrade react-aria (v1.14.0) * refactor(react): group client components * fix(dropdown): keyDown test cases * chore(react): rollback * fix(theme): default transition-duration (#6011) * fix(theme): default transitionDuration * chore(deps): bump @heroui/theme peer dep * ci(changesets): version packages (#5991) Co-authored-by: Junior Garcia <jrgarciadev@gmail.com> * fix(use-image): resolve race condition with cached images on Firefox/Safari (#6041) * fix(use-image): resolve race condition with cached images on Firefox/Safari The bug occurred because event handlers (onload/onerror) were attached AFTER setting the image src. For cached images, browsers fire onload synchronously when src is set, causing the event to be missed and images to remain stuck at opacity-0. Changes: - Attach handlers BEFORE setting src to catch synchronous callbacks - Check both naturalWidth AND naturalHeight (per CodeRabbit review on #4523) - Handle synchronous error callbacks for failed cached images - Add comprehensive test coverage (15 tests) including: - Synchronous cached image success (Firefox/Safari race condition) - Synchronous cached image error - Dynamic src changes - All props (crossOrigin, srcSet, sizes, loading) - Callback invocation verification Reproduction and investigation performed using Claude Opus 4.5. Fixes #4534, #2259 * fix(use-image): add ignoreFallback to useCallback dependencies Address CodeRabbit review feedback: the `ignoreFallback` prop was used inside the `load` callback (line 104) but was missing from the dependency array, creating a stale closure bug. Without this fix, if `ignoreFallback` changes from `true` to `false` dynamically, the `load` callback would retain the stale `true` value, preventing the image from ever loading. Changes: - Add `ignoreFallback` to useCallback dependency array - Add tests for dynamic `ignoreFallback` changes (both directions) - Update changeset to document this fix Verification performed using Claude Opus 4.5. --------- Co-authored-by: Brian Meek <brian@current.space> * fix(docs): broken links in Form page (#6077) * fix(pagination): improve layout for large page counts (#6034) * fix(pagination): improve layout for large page counts/style of paagination compnents * fix(pagination): refine item sizing to balance small and large page numbers * ci(changesets): add pagination sizing changeset * fix(pagination): ensure cursor fully covers button without changing radius * chore(changeset): revise message and add issue numbers --------- Co-authored-by: WK Wong <wingkwong.code@gmail.com> * fix(date-picker): open date-picker when clicking border (#6084) * fix(date-picker): add pointer interaction to open date picker on wrapper click * chore: add changeset * chore(changeset): add issue number --------- Co-authored-by: WK <wingkwong.code@gmail.com> * fix(accordion): click behaviour for dynamically generated accordion items (#6133) * fix(accordion): add collection state * chore: add changeset * fix: update change set * refactor(theme): remove flat dependency (#6157) * chore(deps): remove flat library * refactor(theme): replace flatten from flat * chore: add changeset * fix(listbox): prevent option focus from start/end content slots (#6060) * fix(listbox): prevent option focus from start/end content slots * test: add test code about when clicking non-interactive slot content, and prevented focus leakage from interactive start/end content such as buttons. * docs: add changeset * chore(changeset): add issue number --------- Co-authored-by: WK <wingkwong.code@gmail.com> * fix(system-rsc): extendVariants & compound variants types (#5847) * fix(extendVariants): return component type error * fix(CompoundVariants): correct type inference for extended/compound variants and composition * test: cover compound/extend inference; enforce CP required props * fix(types): correct CompoundVariants class value inference Replaces the conditional ClassProp logic with a simpler, consistent form to fix incorrect slot value inference. Before: ClassProp<S extends undefined ? ClassValue : ClassValue | SlotsClassValue<S>> After: ClassProp<ClassValue | GetSuggestedValues<S>> This ensures GetSuggestedValues<S> is used for slot-aware variants and avoids duplicated conditional branches for undefined slots. * fix(system-rsc): correct slot detection in getSlots() * fix(types): make ExtendVariants props optional and guard V[key] with NonNullable Simplifies the return type of ExtendVariants to ensure no required props are enforced at the HOC level. This aligns with the intended API contract where extended components expose all props as optional. - All keys (CP ∪ V) are optional - Preserve CP type hints and booleanized V values - Added NonNullable<V[key]> guard to prevent undefined indexing * test(extendVariants): add compoundVariants integration test * fix(system-rsc): getSlots() brief JSDoc comment added * test(extendVariants): new styles - extended & fixed styles - original tests for slots component * test(extendVariants): fixed slot component variant styles extended test * fix(types): avoid leaking React internals by removing PropsWithoutRef Replace PropsWithoutRef with explicit Exclude<'ref'> in mapped keys and intersect with RefAttributes<InferRef<C>>. This prevents @types/react’s internal UNDEFINED_VOID_ONLY from leaking into the public .d.ts and fixes declaration emit for components like extended Autocomplete. * chore(changeset): add patch for extendVariants and CompoundVariants type fix * chore(system-rsc): add changeset for getSlots() slot detection fix * refactor(types): unify slot value inference via GetSuggestedValues<S> for consistent variant typing * fix(extendVariants): improved as-prop handling and exclude classNames from SuggestedVariants * fix(system-rsc): add polymorphic 'as' prop support to extendVariants * chore(system-rsc): add missing tests --------- Co-authored-by: doki- <1335902682@qq.com> Co-authored-by: WK Wong <wingkwong.code@gmail.com> * chore(docs): update meta (#6168) * ci(changesets): version packages (#6059) Co-authored-by: Junior Garcia <jrgarciadev@gmail.com> --------- Co-authored-by: Hayato Hasegawa <hase1225hayato@gmail.com> Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: Junior Garcia <jrgarciadev@gmail.com> Co-authored-by: brianatdetections <brian@detections.ai> Co-authored-by: Brian Meek <brian@current.space> Co-authored-by: Dominik Hryshaiev <domhryshaiev@gmail.com> Co-authored-by: creative-atish <149860680+atishkr25@users.noreply.github.com> Co-authored-by: KyZy7 <29321162+KyZy7@users.noreply.github.com> Co-authored-by: Deepansh Bhargava <deepansh940@gmail.com> Co-authored-by: KumJungMin <37934668+KumJungMin@users.noreply.github.com> Co-authored-by: Bohdan Kulinchenko <35272606+ITBoomBKStudio@users.noreply.github.com> Co-authored-by: doki- <1335902682@qq.com>
* fix(theme): hide password reveal button (#5990) * fix(link): link overriding role (#5999) * fix(link): allow overriding role * chore(link): changeset * chore(deps): upgrade react-aria (v1.14.0) (#5996) * chore(deps): upgrade react-aria (v1.14.0) * refactor(react): group client components * fix(dropdown): keyDown test cases * chore(react): rollback * fix(theme): default transition-duration (#6011) * fix(theme): default transitionDuration * chore(deps): bump @heroui/theme peer dep * ci(changesets): version packages (#5991) Co-authored-by: Junior Garcia <jrgarciadev@gmail.com> * fix(use-image): resolve race condition with cached images on Firefox/Safari (#6041) * fix(use-image): resolve race condition with cached images on Firefox/Safari The bug occurred because event handlers (onload/onerror) were attached AFTER setting the image src. For cached images, browsers fire onload synchronously when src is set, causing the event to be missed and images to remain stuck at opacity-0. Changes: - Attach handlers BEFORE setting src to catch synchronous callbacks - Check both naturalWidth AND naturalHeight (per CodeRabbit review on #4523) - Handle synchronous error callbacks for failed cached images - Add comprehensive test coverage (15 tests) including: - Synchronous cached image success (Firefox/Safari race condition) - Synchronous cached image error - Dynamic src changes - All props (crossOrigin, srcSet, sizes, loading) - Callback invocation verification Reproduction and investigation performed using Claude Opus 4.5. Fixes #4534, #2259 * fix(use-image): add ignoreFallback to useCallback dependencies Address CodeRabbit review feedback: the `ignoreFallback` prop was used inside the `load` callback (line 104) but was missing from the dependency array, creating a stale closure bug. Without this fix, if `ignoreFallback` changes from `true` to `false` dynamically, the `load` callback would retain the stale `true` value, preventing the image from ever loading. Changes: - Add `ignoreFallback` to useCallback dependency array - Add tests for dynamic `ignoreFallback` changes (both directions) - Update changeset to document this fix Verification performed using Claude Opus 4.5. --------- Co-authored-by: Brian Meek <brian@current.space> * fix(docs): broken links in Form page (#6077) * fix(pagination): improve layout for large page counts (#6034) * fix(pagination): improve layout for large page counts/style of paagination compnents * fix(pagination): refine item sizing to balance small and large page numbers * ci(changesets): add pagination sizing changeset * fix(pagination): ensure cursor fully covers button without changing radius * chore(changeset): revise message and add issue numbers --------- Co-authored-by: WK Wong <wingkwong.code@gmail.com> * fix(date-picker): open date-picker when clicking border (#6084) * fix(date-picker): add pointer interaction to open date picker on wrapper click * chore: add changeset * chore(changeset): add issue number --------- Co-authored-by: WK <wingkwong.code@gmail.com> * fix(accordion): click behaviour for dynamically generated accordion items (#6133) * fix(accordion): add collection state * chore: add changeset * fix: update change set * refactor(theme): remove flat dependency (#6157) * chore(deps): remove flat library * refactor(theme): replace flatten from flat * chore: add changeset * fix(listbox): prevent option focus from start/end content slots (#6060) * fix(listbox): prevent option focus from start/end content slots * test: add test code about when clicking non-interactive slot content, and prevented focus leakage from interactive start/end content such as buttons. * docs: add changeset * chore(changeset): add issue number --------- Co-authored-by: WK <wingkwong.code@gmail.com> * fix(system-rsc): extendVariants & compound variants types (#5847) * fix(extendVariants): return component type error * fix(CompoundVariants): correct type inference for extended/compound variants and composition * test: cover compound/extend inference; enforce CP required props * fix(types): correct CompoundVariants class value inference Replaces the conditional ClassProp logic with a simpler, consistent form to fix incorrect slot value inference. Before: ClassProp<S extends undefined ? ClassValue : ClassValue | SlotsClassValue<S>> After: ClassProp<ClassValue | GetSuggestedValues<S>> This ensures GetSuggestedValues<S> is used for slot-aware variants and avoids duplicated conditional branches for undefined slots. * fix(system-rsc): correct slot detection in getSlots() * fix(types): make ExtendVariants props optional and guard V[key] with NonNullable Simplifies the return type of ExtendVariants to ensure no required props are enforced at the HOC level. This aligns with the intended API contract where extended components expose all props as optional. - All keys (CP ∪ V) are optional - Preserve CP type hints and booleanized V values - Added NonNullable<V[key]> guard to prevent undefined indexing * test(extendVariants): add compoundVariants integration test * fix(system-rsc): getSlots() brief JSDoc comment added * test(extendVariants): new styles - extended & fixed styles - original tests for slots component * test(extendVariants): fixed slot component variant styles extended test * fix(types): avoid leaking React internals by removing PropsWithoutRef Replace PropsWithoutRef with explicit Exclude<'ref'> in mapped keys and intersect with RefAttributes<InferRef<C>>. This prevents @types/react’s internal UNDEFINED_VOID_ONLY from leaking into the public .d.ts and fixes declaration emit for components like extended Autocomplete. * chore(changeset): add patch for extendVariants and CompoundVariants type fix * chore(system-rsc): add changeset for getSlots() slot detection fix * refactor(types): unify slot value inference via GetSuggestedValues<S> for consistent variant typing * fix(extendVariants): improved as-prop handling and exclude classNames from SuggestedVariants * fix(system-rsc): add polymorphic 'as' prop support to extendVariants * chore(system-rsc): add missing tests --------- Co-authored-by: doki- <1335902682@qq.com> Co-authored-by: WK Wong <wingkwong.code@gmail.com> * chore(docs): update meta (#6168) * ci(changesets): version packages (#6059) Co-authored-by: Junior Garcia <jrgarciadev@gmail.com> --------- Co-authored-by: Hayato Hasegawa <hase1225hayato@gmail.com> Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: Junior Garcia <jrgarciadev@gmail.com> Co-authored-by: brianatdetections <brian@detections.ai> Co-authored-by: Brian Meek <brian@current.space> Co-authored-by: Dominik Hryshaiev <domhryshaiev@gmail.com> Co-authored-by: creative-atish <149860680+atishkr25@users.noreply.github.com> Co-authored-by: KyZy7 <29321162+KyZy7@users.noreply.github.com> Co-authored-by: Deepansh Bhargava <deepansh940@gmail.com> Co-authored-by: KumJungMin <37934668+KumJungMin@users.noreply.github.com> Co-authored-by: Bohdan Kulinchenko <35272606+ITBoomBKStudio@users.noreply.github.com> Co-authored-by: doki- <1335902682@qq.com>
* fix(theme): hide password reveal button (#5990) * fix(link): link overriding role (#5999) * fix(link): allow overriding role * chore(link): changeset * chore(deps): upgrade react-aria (v1.14.0) (#5996) * chore(deps): upgrade react-aria (v1.14.0) * refactor(react): group client components * fix(dropdown): keyDown test cases * chore(react): rollback * fix(theme): default transition-duration (#6011) * fix(theme): default transitionDuration * chore(deps): bump @heroui/theme peer dep * ci(changesets): version packages (#5991) Co-authored-by: Junior Garcia <jrgarciadev@gmail.com> * fix(use-image): resolve race condition with cached images on Firefox/Safari (#6041) * fix(use-image): resolve race condition with cached images on Firefox/Safari The bug occurred because event handlers (onload/onerror) were attached AFTER setting the image src. For cached images, browsers fire onload synchronously when src is set, causing the event to be missed and images to remain stuck at opacity-0. Changes: - Attach handlers BEFORE setting src to catch synchronous callbacks - Check both naturalWidth AND naturalHeight (per CodeRabbit review on #4523) - Handle synchronous error callbacks for failed cached images - Add comprehensive test coverage (15 tests) including: - Synchronous cached image success (Firefox/Safari race condition) - Synchronous cached image error - Dynamic src changes - All props (crossOrigin, srcSet, sizes, loading) - Callback invocation verification Reproduction and investigation performed using Claude Opus 4.5. Fixes #4534, #2259 * fix(use-image): add ignoreFallback to useCallback dependencies Address CodeRabbit review feedback: the `ignoreFallback` prop was used inside the `load` callback (line 104) but was missing from the dependency array, creating a stale closure bug. Without this fix, if `ignoreFallback` changes from `true` to `false` dynamically, the `load` callback would retain the stale `true` value, preventing the image from ever loading. Changes: - Add `ignoreFallback` to useCallback dependency array - Add tests for dynamic `ignoreFallback` changes (both directions) - Update changeset to document this fix Verification performed using Claude Opus 4.5. --------- Co-authored-by: Brian Meek <brian@current.space> * fix(docs): broken links in Form page (#6077) * fix(pagination): improve layout for large page counts (#6034) * fix(pagination): improve layout for large page counts/style of paagination compnents * fix(pagination): refine item sizing to balance small and large page numbers * ci(changesets): add pagination sizing changeset * fix(pagination): ensure cursor fully covers button without changing radius * chore(changeset): revise message and add issue numbers --------- Co-authored-by: WK Wong <wingkwong.code@gmail.com> * fix(date-picker): open date-picker when clicking border (#6084) * fix(date-picker): add pointer interaction to open date picker on wrapper click * chore: add changeset * chore(changeset): add issue number --------- Co-authored-by: WK <wingkwong.code@gmail.com> * fix(accordion): click behaviour for dynamically generated accordion items (#6133) * fix(accordion): add collection state * chore: add changeset * fix: update change set * refactor(theme): remove flat dependency (#6157) * chore(deps): remove flat library * refactor(theme): replace flatten from flat * chore: add changeset * fix(listbox): prevent option focus from start/end content slots (#6060) * fix(listbox): prevent option focus from start/end content slots * test: add test code about when clicking non-interactive slot content, and prevented focus leakage from interactive start/end content such as buttons. * docs: add changeset * chore(changeset): add issue number --------- Co-authored-by: WK <wingkwong.code@gmail.com> * fix(system-rsc): extendVariants & compound variants types (#5847) * fix(extendVariants): return component type error * fix(CompoundVariants): correct type inference for extended/compound variants and composition * test: cover compound/extend inference; enforce CP required props * fix(types): correct CompoundVariants class value inference Replaces the conditional ClassProp logic with a simpler, consistent form to fix incorrect slot value inference. Before: ClassProp<S extends undefined ? ClassValue : ClassValue | SlotsClassValue<S>> After: ClassProp<ClassValue | GetSuggestedValues<S>> This ensures GetSuggestedValues<S> is used for slot-aware variants and avoids duplicated conditional branches for undefined slots. * fix(system-rsc): correct slot detection in getSlots() * fix(types): make ExtendVariants props optional and guard V[key] with NonNullable Simplifies the return type of ExtendVariants to ensure no required props are enforced at the HOC level. This aligns with the intended API contract where extended components expose all props as optional. - All keys (CP ∪ V) are optional - Preserve CP type hints and booleanized V values - Added NonNullable<V[key]> guard to prevent undefined indexing * test(extendVariants): add compoundVariants integration test * fix(system-rsc): getSlots() brief JSDoc comment added * test(extendVariants): new styles - extended & fixed styles - original tests for slots component * test(extendVariants): fixed slot component variant styles extended test * fix(types): avoid leaking React internals by removing PropsWithoutRef Replace PropsWithoutRef with explicit Exclude<'ref'> in mapped keys and intersect with RefAttributes<InferRef<C>>. This prevents @types/react’s internal UNDEFINED_VOID_ONLY from leaking into the public .d.ts and fixes declaration emit for components like extended Autocomplete. * chore(changeset): add patch for extendVariants and CompoundVariants type fix * chore(system-rsc): add changeset for getSlots() slot detection fix * refactor(types): unify slot value inference via GetSuggestedValues<S> for consistent variant typing * fix(extendVariants): improved as-prop handling and exclude classNames from SuggestedVariants * fix(system-rsc): add polymorphic 'as' prop support to extendVariants * chore(system-rsc): add missing tests --------- Co-authored-by: doki- <1335902682@qq.com> Co-authored-by: WK Wong <wingkwong.code@gmail.com> * chore(docs): update meta (#6168) * ci(changesets): version packages (#6059) Co-authored-by: Junior Garcia <jrgarciadev@gmail.com> * fix(system-rsc): extendVariants rendering behavior with as (#6215) * fix(system-rsc): fix components rendering with 'as' prop * fix(system-rsc): fix compoundVariants and slots inheritance in extendVariants * fix(system-rsc): extendVariants test file cleaned * chore(deps): bump RA dependencies (#6221) * chore(deps): bump RA dependencies * chore(date-picker): revise test cases * fix(button): correct disableRipple prop precedence (#6199) * ci(changesets): version packages (#6227) Co-authored-by: Junior Garcia <jrgarciadev@gmail.com> --------- Co-authored-by: Hayato Hasegawa <hase1225hayato@gmail.com> Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: Junior Garcia <jrgarciadev@gmail.com> Co-authored-by: brianatdetections <brian@detections.ai> Co-authored-by: Brian Meek <brian@current.space> Co-authored-by: Dominik Hryshaiev <domhryshaiev@gmail.com> Co-authored-by: creative-atish <149860680+atishkr25@users.noreply.github.com> Co-authored-by: KyZy7 <29321162+KyZy7@users.noreply.github.com> Co-authored-by: Deepansh Bhargava <deepansh940@gmail.com> Co-authored-by: KumJungMin <37934668+KumJungMin@users.noreply.github.com> Co-authored-by: Bohdan Kulinchenko <35272606+ITBoomBKStudio@users.noreply.github.com> Co-authored-by: doki- <1335902682@qq.com> Co-authored-by: Chris Nowak <krzysztofmareknowak@gmail.com>
Closes #4534
Closes #2259
Summary
This PR fixes a race condition in the
use-imagehook that caused cached images to remain invisible (stuck atopacity-0) on Firefox and Safari. The issue has been reported multiple times and persists despite previous fix attempts.Root Cause
The bug occurred because event handlers (
onload/onerror) were attached in a separateuseEffectthat ran after setting the imagesrc. For cached images, browsers may fireonloadsynchronously whensrcis set, causing the event to be missed entirely since the handler wasn't attached yet.Solution
Attach handlers before setting
src, ensuring callbacks are caught even for synchronously-loaded cached images:Additional improvements:
naturalWidthandnaturalHeightper feedback on PR fix(use-image): load images after props change #4523Test Coverage
Added 15 tests (previously 5) covering:
srcchanges (undefined → valid, valid → different valid)ignoreFallback,shouldBypassImageLoad)onLoad,onError)crossOrigin,srcSet,sizes,loading)Verification
Breaking Changes
None. The hook's public API remains unchanged.
Thank you for maintaining HeroUI! Please let me know if you have any questions or would like any changes to this PR.
Summary by CodeRabbit
Bug Fixes
Tests
✏️ Tip: You can customize this high-level summary in your review settings.