gilbarbara / gilbarbara/react-inlinesvg

Math.random() on every render breaks static prerendering (Next.js cacheComponents)

Open Beginner friendly
#253 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
TypeScript
Stars
1.3k
Forks
105
PR merge metrics
No merged PRs in 30d

Description

Summary

useInlineSVG calls Math.random() on every render, which makes the component impossible to statically prerender in Next.js 16 with cacheComponents enabled.

src/modules/useInlineSVG.ts:27

const hash = useRef(uniqueHash ?? randomString(8));

useRef's argument is evaluated eagerly on every render, and ?? evaluates its right operand whenever uniqueHash is undefined — so randomString(8) runs each time even though the ref only keeps the first result. v3 did the same thing in the class constructor; 4.x moved it into the hook.

Why it breaks prerendering

Next.js 16 prerenders client components at build time and stores the HTML. Under cacheComponents it patches the non-deterministic globals to detect values it cannot reproduce — node_modules/next/dist/server/node-environment-extensions/random.js:

We extend Math.random() during builds and revalidates to ensure that prerenders don't observe randomness

A patched call aborts the enclosing Suspense boundary. Next then takes its "postponed but nothing dynamic was used" path and freezes every boundary from that point on as client-rendered in the stored artifact. There is no request-time recovery.

The failure is silent — the abort is deliberately transparent to the caller — so a well-structured app just serves a near-empty document. On our site every prerendered page dropped to a 487-byte shell, and the header logo was the first thing to trip it.

Reproduction
// app/probe/Probe.tsx
'use client';
import SVG from 'react-inlinesvg';

export const Probe = () => <SVG src="https://example.com/logo.svg" />;
// next.config.mjs
export default { cacheComponents: true };

next build:

Route "/probe": Next.js encountered the unstable value `Math.random()` in a Client Component.
Error occurred prerendering page "/probe"

Note this is invisible in next dev — dev renders every affected page correctly.

Observation

The hash is only read when uniquifyIDs is set (getNode returns early otherwise at utils.ts:59, and updateSVGAttributes at utils.ts:115), and only once content exists — which requires the DOM, since loading happens in the mount effect behind canUseDOM(). So the hash can never be needed during a server render, and generating it there is pure waste.

Generating it on first read instead of on every render fixes this without changing any behaviour or dropping React 16.8/17 support (useId would need React 18+).

Workaround for anyone hitting this now

Pass a stable uniqueHash at every call site:

<SVG src={src} uniqueHash={useId()} />

Happy to open a PR — I have the fix and a regression test ready. Filing this first per CONTRIBUTING.md.

Environment
  • react-inlinesvg 4.5.0 (also 4.2.0, 4.4.1, 3.0.3)
  • Next.js 16.3.1, React 19.2.3

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with src/modules/useInlineSVG.ts:27, then inspect utils.ts at lines 59 and 115 to confirm when the hash is read. Reproduce the failure with the provided Next.js cacheComponents example and run the available regression tests. Done means Math.random() is not evaluated during renders, static prerendering succeeds, and existing behavior remains unchanged.

Written by the indexing model from the issue text.

Assessment

Tech stack
next.js, react, typescript
Domain
build-system, frontend
Issue type
Bug
Difficulty
2/5
Estimated time
1-3 hours
Activity status
Active
Clarity
Clearly specified
Newbie friendliness
87/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.