Skip to content

Reference · components

Upstream bugs in component libraries, grouped by pattern

components/references/upstream-bugs.md174 linesupdated 16 Sept 2026

These 48 bugs were found by reading registry source (fetched 2026-09-16) and in shipped apps. They are grouped by pattern: when you audit any registry component, scan for each pattern's signature, not just for the named components. Lint ids are void lint rules that catch the pattern statically.

Format: Library / component: the bug → the impact. The fix pattern closes each group.

1. Dead on Tailwind v4 (silent: no error, the effect never shows)

Signature: animate- classes with no css/cssVars in the registry item; keyframes in tailwind.config.* or in comments; v3 utility names; undefined theme classes.

  • Aceternity spotlight: animate-spotlight ships with no CSS, and the base class is opacity-0 → invisible forever.
  • Aceternity meteors, moving-border (and other custom animate-* items): no CSS in the registry item → no animation.
  • React Bits StarBorder: keyframes exist only as a commented tailwind.config.js block → static.
  • Registries generally: bg-gradient-to-*, flex-shrink-0, bare shadow/rounded, outline-none, config keyframes → gradients missing, sizes one step off.
  • Cult direction-aware-tabs: undefined shadow-inner-shadow → silent style loss.
  • kibo gantt: undefined --color-backdrop → silent style loss.
  • ReUI frame: served JSON lost variant prefixes → dead variants.

Fix pattern: put @keyframes + a class in @layer components, or --animate-x + @keyframes in @theme. Rename v3 utilities (bg-linear-to-*, shrink-0, shadow-xs, outline-hidden). Run next build and confirm the class exists in the compiled CSS. Lint: lint/tw-unknown-class, lint/tw-js-config-ignored, lint/tw-v3-renamed, lint/tw-v3-arbitrary-var.

2. Hidden or placeholder server HTML (SEO, no-JS, LCP)

Signature: opacity-0, initial={{ opacity: 0 }} or initial="hidden" on text; a number component rendering 0/""/startValue; GSAP set after hydration.

  • Magic UI number-ticker: SSR renders startValue (0), and a width change from 0 to 12,400 causes CLS → crawlers and link previews see 0.
  • React Bits CountUp: SSR renders an empty → no number without JS.
  • Aceternity text-generate-effect: every word SSRs at opacity-0 + blur(10px) → invisible without JS; LCP delayed to the end of a 0.2s × N stagger.
  • Magic UI text-animate: initial="hidden" → text at opacity 0 before hydration.
  • React Bits SplitText: full text on the server, then GSAP sets opacity:0; y:40 after fonts.ready → flash, hide, reveal; LCP moves.
  • React Bits SplitText: willChange: "transform, opacity" left on the parent after completion; pulls gsap + ScrollTrigger + SplitText (~45–70 KB) for one headline → a permanent layer; weight. Lint: lint/will-change-static.

Fix pattern: SSR the final value and visible text. Animate position, not visibility, on LCP text (RevealText). Numbers animate only if they start offscreen (NumberTicker, or @number-flow/react).

3. SSR crashes and hydration mismatches

Signature: window/document/navigator/matchMedia/localStorage at module scope or in render; Math.random()/Date.now() in render; createPortal(document.body) in render.

  • Skiper106: navigator.userAgent at module scope → SSR crash.
  • Aceternity background-beams: Math.random() in render for duration, delay and y2 → hydration mismatch, and values re-randomise each render.
  • Aceternity (26 of 118 free items): Math.random() in render → hydration risk.
  • Magic UI globe: setTimeout(() => style.opacity = "1", 0) → a hydration-timing hack.
  • React Bits (all 171 files): no 'use client' → RSC import errors.

Fix pattern: browser globals only inside effects or useSyncExternalStore (server snapshot). Seeded or precomputed values instead of Math.random() in render. 'use client' only on the interactive leaf. Lint: lint/browser-global-in-render.

4. Leaks: subscriptions and loops without cleanup

Signature: .on('change' or addEventListener outside useEffect; requestAnimationFrame without cancelAnimationFrame; new Lenis without destroy; api.on without off; setInterval per instance.

  • Skiper34, Skiper37: scrollY.on('change') in the render body → a new listener every render.
  • Skiper30 (+16/17/28/31/34): the Lenis rAF loop is never cancelled and lenis.destroy() is missing → a permanent rAF after unmount.
  • Skiper54: api.on('select') is never removed → listener leak.
  • kibo relative-time: a setInterval per instance, never paused → N timers.

Fix pattern: useMotionValueEvent or an effect with an unsubscribe. Match every add, observe, rAF, interval or create with its remove, disconnect, cancel, clear or destroy. One shared ticker for lists. No Lenis. Lint: lint/motionvalue-subscribe-in-render, lint/raf-without-cancel, lint/listener-without-cleanup, lint/timer-in-render.

5. Hooks misuse

Signature: use[A-Z]…( inside &&, ternaries, JSX or style={{}}; m.create() or component definitions in render; ref.current in deps; props already read from refs listed in effect deps.

  • Magic UI magic-card: useMotionTemplate inside {mode === "gradient" && …} → React throws when mode changes.
  • Skiper19: useTransform inside a style={{}} literal → rules-of-hooks violation.
  • tailark animated-group: m.create() in render → remount and replay every render.
  • Aceternity text-generate-effect: [scope.current] as an effect dep → a stale or ineffective effect.
  • React Bits Aurora: deps [amplitude] although the value is read from a ref → destroys and rebuilds the WebGL context on prop change.
  • Magic UI globe: config object in effect deps → inline config={{…}} recreates the globe every render.
  • Cult direction-aware-tabs: hardcoded layoutId="bubble" → two instances fight.

Fix pattern: hooks at the top level; useMemo for created components; useId()-scoped layoutId; stable primitives in deps; update uniforms instead of rebuilding.

6. Per-frame React work and allocation

Signature: setState inside mousemove/pointermove/scroll/rAF/useAnimationFrame; new Intl.NumberFormat, new Color or getBoundingClientRect inside loops; window listeners per instance.

  • React Bits Magnet: a window mousemove per instance + getBoundingClientRect + setState on every move → N listeners, layout reads and re-renders per event.
  • React Bits SpotlightCard: setState per move and a repainted full-card radial-gradient → re-renders children every frame.
  • Magic UI number-ticker, React Bits CountUp: a new Intl.NumberFormat per frame, locale hardcoded to en-US → GC churn; wrong locale.
  • React Bits Aurora: new Color() ×3 per frame → ~420 allocations/s.
  • Aceternity dotted-glow-background: getBoundingClientRect inside draw + shadowBlur per dot → forced layout at 60fps.
  • ReUI scrollspy: capture-phase scroll + a forced layout loop; its throttleTime prop is unused → scroll jank.
  • Magic UI number-ticker: useSpring with damping 60 → slow settle (the top idle cost in a shipped app).

Fix pattern: refs + one rAF writing el.style.translate (SpotlightCard, Magnetic); measure once on enter or in a ResizeObserver; hoist formatters; IntersectionObserver instead of scroll listeners; near-critical springs.

7. Never stops: offscreen spin and no visibility pause

Signature: repeat: Infinity, infinite, useAnimationFrame or a rAF loop with no IntersectionObserver, useInView or data-fx; WebGL libraries' internal loops.

  • Magic UI border-beam: motion animates offset-distance on the main thread forever → idle CPU.
  • motion-primitives border-trail, tailark infinite-slider: repeat: Infinity, never paused → idle main thread.
  • React Bits ShinyText: a perpetual useAnimationFrame writing background-position (still ticking when disabled) → a paint every frame per instance.
  • Magic UI globe: cobe's rAF runs until unmount; no IntersectionObserver or visibility pause → GPU burn.
  • Magic UI animated-beam: repeat = Infinity JS gradient animation → a repaint every frame.
  • Magic UI animated-beam: its ResizeObserver watches only the container; endpoints that move (font swap, image load, accordion) leave the beam pointing at stale coordinates.
  • React Bits backgrounds: 35/56 have no offscreen pause, 53/56 no reduced motion, 23/56 no DPR cap → GPU burn; WCAG 2.3.3.
  • Aceternity spotlight-new: infinite motion + z-40 → covers content and never rests.

Fix pattern: CSS animation + data-fx + FxGate (recipes §0). JS loops cancel (not skip) on IntersectionObserver and visibilitychange. Lint: smooth/raf-loop-idle.

8. Paint-bound or layout-bound animation

Signature: animating background, background-position, filter, box-shadow, SVG gradient attributes, width/height/top/left; many stacked backdrop-filters; mask-image over animated children.

  • RareUI HookSidebar: springs top/height → layout every frame.
  • Magic UI animated-beam: animates x1/x2/y1/y2 of a linearGradient → path repaint per frame.
  • Aceternity background-beams: 50 SVG paths × animated gradients, no IntersectionObserver or RM → CPU raster.
  • Magic UI magic-card (orb mode): filter: blur(60px) on a 420px element moving with the pointer → a huge blurred layer per frame.
  • Aceternity text-generate-effect: filter: blur animated per word → rasterisation per word per frame.
  • tailark progressive-blur: 8 stacked backdrop-filter layers → 8–15ms per frame.
  • kibo gantt: 5× backdrop-blur on sticky elements → scroll jank.
  • void measurement (recipes §8–9): a mask-image fade over animated blobs → 10% dropped frames; a paint animation over an animated backdrop without its own layer → 14–46%.

Fix pattern: move pre-rasterised layers with translate/rotate (BeamBorder, AuroraBackdrop, SpotlightCard); one blur + one mask; a static overlay instead of a mask over animation; will-change: transform on a single small paint-animated element. Lint: lint/animate-layout-prop, lint/transition-all, smooth/animate-layout-property.

9. DOM and node explosions

Signature: Array(n).fill().map of animated nodes; per-character motion spans; SVG grids.

  • Magic UI dot-pattern (glow): ~5,130 s, each with an infinite spring → DOM and animation explosion.
  • Magic UI animated-grid-pattern: the same node explosion.
  • motion-primitives text-effect: per-character motion nodes by default → ~60 nodes per headline.

Fix pattern: CSS gradients (PatternBackdrop); per="word" or CSS per-word (RevealText).

10. WebGL, canvas and expensive dependencies

Signature: a getContext('webgl')//createGlobe per instance or in .map(); DPR 2 hardcoded; three/R3F for a 2D quad; heavy or stale dependencies.

  • RareUI FluidOrb: one WebGL context per instance, uncapped rAF, DPR 2 → the 9th context evicts the 1st in lists.
  • React Bits Silk: three + R3F (~150 KB) for one quad, frameloop="always" → the worst cost/benefit.
  • React Bits GridScan: imports face-api.js (~700 KB + weights) → a bundle bomb.
  • Aceternity globe/3d-globe/canvas-reveal-effect/vortex: three-globe or R3F, one context each → 600 KB+.
  • Magic UI globe: pins cobe@^0.6.4 (current 2.0.1); devicePixelRatio: 2 and width * 2 hardcoded; mapSamples: 16000 → a stale major; 4× pixels on DPR-1 screens.
  • Magic UI globe: drag uses onPointerOut with no setPointerCapture → the drag drops at the canvas edge.
  • Animate UI (17 items): depends on @base-ui-components/react (renamed to @base-ui/react) → a duplicate, older Base UI.
  • shadcnblocks: import { cn } from "cn" + a "cn" dependency → phantom dependency.
  • ReUI tree/rating: site-internal @/app/(create)/… imports → build break.

Fix pattern: ≤1 context per page, never in lists (static seeded frame for many instances); DPR ≤1.5; ≤30fps; ogl or raw WebGL2 per speedreferences/rendering-smoothness.md §8; cobe 2.x for globes; diff registry dependencies against package.json. Lint: lint/webgl-in-map, lint/heavy-import, smooth/multiple-webgl-contexts.

11. Accessibility

Signature: duplicated content without aria-hidden/inert; aria-label on generic elements; clickable

  • /
    ;