---
title: "`new Intl.NumberFormat` / `Intl.NumberFormat(` / `new Color(` inside an animation-frame callback (lint/allocation-per-frame)"
description: "lint/allocation-per-frame: Number tickers (Magic UI NumberTicker, React Bits CountUp) build a formatter on every frame of a spring; WebGL backgrounds (React…"
canonical: https://void-design.vercel.app/rules/lint/allocation-per-frame
lastModified: 2026-09-16
---

# `new Intl.NumberFormat` / `Intl.NumberFormat(` / `new Color(` inside an animation-frame callback

`lint/allocation-per-frame` · severity **warn** · category Lint · detected by `void lint` and `void audit`

## Why it matters

Number tickers (Magic UI NumberTicker, React Bits CountUp) build a formatter on every frame of a spring; WebGL backgrounds (React Bits Aurora) allocate colours per frame. Formatter construction costs ~50–100× a `format()` call and the garbage causes GC pauses mid-animation.

## How to fix it

Create formatters and colour objects once (module scope or `useMemo`) and only call `.format()` / mutate inside `requestAnimationFrame`, `useAnimationFrame`, `.on('change')` or `useMotionValueEvent` callbacks.

## Example

```tsx
const fmt = useMemo(() => new Intl.NumberFormat(locale, { maximumFractionDigits: 0 }), [locale]);
useMotionValueEvent(spring, 'change', (v) => { if (ref.current) ref.current.textContent = fmt.format(v); });
```

## More lint rules

`void lint` reports 67 rules in this category. Static source checks with no browser: Tailwind v4 silent failures, Next.js 16 API traps, React render-body bugs, accessibility markup, SEO files and motion hygiene.

- `animate-undefined` `animate-*` class with no matching `--animate-*` theme variable or `@keyframes` — [lint/animate-undefined](https://void-design.vercel.app/rules/lint/animate-undefined)
- `random-in-render` `Math.random()` / `Date.now()` / `new Date()` evaluated in a component render body — [lint/random-in-render](https://void-design.vercel.app/rules/lint/random-in-render)
- `duplicate-children-not-hidden` Children repeated for a marquee/loop without `aria-hidden` or `inert` on the copies — [lint/duplicate-children-not-hidden](https://void-design.vercel.app/rules/lint/duplicate-children-not-hidden)
- `conditional-hook` Hook called inside a JSX expression, `&&`, ternary or callback — [lint/conditional-hook](https://void-design.vercel.app/rules/lint/conditional-hook)
- `svg-global-id` Hardcoded `id` on an SVG `<filter>`, gradient, `<clipPath>` or `<mask>` inside a reusable component — [lint/svg-global-id](https://void-design.vercel.app/rules/lint/svg-global-id)
- `setstate-per-pointer-move` React state set inside a `mousemove` / `pointermove` / `scroll` handler — [lint/setstate-per-pointer-move](https://void-design.vercel.app/rules/lint/setstate-per-pointer-move)
- `hardcoded-device-pixel-ratio` Canvas/WebGL renders with a literal `devicePixelRatio: 2` (or `width * 2`) — [lint/hardcoded-device-pixel-ratio](https://void-design.vercel.app/rules/lint/hardcoded-device-pixel-ratio)
- `button-missing-type` `<button>` (or `as="button"` default) without an explicit `type` — [lint/button-missing-type](https://void-design.vercel.app/rules/lint/button-missing-type)

Detected by `void lint` and `void audit`. Explain it in a terminal: `void rules lint/allocation-per-frame`
