@react-dnd/invariant review
@react-dnd/invariant 4.0.2 is a one-function runtime assertion extracted for React DnD's internal packages. `invariant(condition, message, ...values)` returns when the condition is truthy. Otherwise it throws an `Error` named `Invariant Violation` after replacing `%s` markers in the message. It performs no drag-and-drop work and does not validate external data. Our measured full import was only 0.4 KB gzipped, with no dependencies or peers. The current npm release is still the April 2022 ESM package, so its 4.7 million weekly downloads mostly describe React DnD's dependency graph, not a reason to choose it directly.
@react-dnd/invariant 4.0.2 installed as 1 package and bundled to 0.4 KB gzipped in our sandbox, but its npm release dates to 2022 and its types do not narrow assertions. Do not add it for its download count; keep it only where exact React DnD error compatibility is already part of the code.
We installed it
| Install | ✓ · 0.9s | 1 package on disk · 1 MB |
| Import | ✓ | ESM import works · require() works · ESM package |
| Browser | 0.4 KB | gzipped (0.6 KB minified), bundled with esbuild |
| Types | ✓ | TypeScript types bundled |
| Known vulns | 0 | 0 critical · 0 high · 0 moderate · 0 low (npm audit) |
Answers from our run
Does @react-dnd/invariant install cleanly?
Yes. In a fresh container with an empty cache, npm install @react-dnd/invariant finished in 0.9s, leaving 1 package and 1 MB on disk. npm audit reported no known vulnerabilities.
How much does @react-dnd/invariant add to a browser bundle?
0.4 KB gzipped (0.6 KB minified) when the whole package is bundled for the browser with esbuild. Importing only part of it is usually smaller.
Does @react-dnd/invariant work with both ESM and CommonJS?
Yes. Both import '@react-dnd/invariant' and require('@react-dnd/invariant') worked in Node 22 in our run. The package is published as ESM.
Does @react-dnd/invariant include TypeScript types?
Yes, type declarations ship inside the package, so no @types install is needed.
@react-dnd/invariant or tiny-invariant: which should you use?
tiny-invariant: Choose it for a public standalone invariant helper with direct-use documentation. @react-dnd/invariant 4.0.2 installed as 1 package and bundled to 0.4 KB gzipped in our sandbox, but its npm release dates to 2022 and its types do not narrow assertions.
When should you not use @react-dnd/invariant?
You are picking an assertion helper for new application code; this package has no README or package-specific documentation
Use it if
- You maintain React DnD code and must preserve its exact error name and `%s` message substitution
- Existing tests already depend on `Invariant Violation` rather than a project-owned assertion class
- A tiny runtime check is enough and TypeScript control-flow narrowing is not required
- You are picking an assertion helper for new application code; this package has no README or package-specific documentation
- TypeScript should narrow a value after the check; 4.0.2's declaration returns `void` rather than `asserts condition`
- A CommonJS-oriented toolchain requires a conventional dual package; 4.0.2 declares ESM and has no exports map
- Messages need formatting beyond `%s`; other printf tokens are left alone and extra values are ignored
- You are validating requests, config, or files; a schema validator can report field-level errors instead of throwing one generic internal failure
Setup reality
Our install of @react-dnd/invariant 4.0.2 completed in 0.9 seconds in a clean Node 22 container. It left 1 package using 1 MB, and npm audit reported 0 known vulnerabilities. The package itself was 32 KB unpacked, with 0 direct dependencies and 0 peer dependencies. It declares ESM and has no exports map, yet both require() and ESM import worked in our sandbox. TypeScript declarations are bundled. A full esbuild browser import measured 0.6 KB minified and 0.4 KB gzipped.
There are no credentials, config files, native builds, or runtime setup. Import the named invariant function and always pass a message. The public declaration accepts any condition and returns void; it does not tell TypeScript that a nullable value is now defined. Add a small local wrapper with an asserts value is NonNullable<T> signature if an existing codebase must keep this dependency.
Message formatting replaces %s tokens in order. Too few values put undefined into the text, while surplus values do nothing. The thrown value is a normal Error with its name changed to Invariant Violation; the package exports no dedicated error class. Catching by a name string is fragile, so use invariants for programmer mistakes that should surface, not for expected user-input branches.
The current release is 4.0.2 from April 2022. The wider React DnD repository moved later, but there is no newer npm package to carry those workspace changes. At 0.4 KB gzipped, bundle size is not the objection. The weak direct-use docs, old publication, and missing assertion signature are. A local 4-line assertion often gives a clearer ownership and typing story.
Patterns
Stop on an impossible state assert-condition
import {invariant} from '@react-dnd/invariant';
invariant(items.length > 0, 'Expected at least one item');
process(items[0]);A false condition throws synchronously with the name `Invariant Violation`; this is for internal bugs.
Insert one value in the message format-one-value
invariant(handlers.has(id), 'Expected handler %s to be registered', id);Only `%s` is replaced; `%d` and named placeholders have no formatting behavior.
Insert values in order format-two-values
invariant(source === target, 'Cannot connect %s to %s', source, target);Two `%s` markers consume 2 arguments. A missing value becomes the word `undefined`.
Wrap it with an assertion signature add-type-narrowing
function assertDefined<T>(value: T, label: string): asserts value is NonNullable<T> {
invariant(value != null, '%s must be defined', label);
}
assertDefined(user, 'user');
console.log(user.id);The local `asserts` signature narrows the value because version 4.0.2's own declaration does not.
Guard an indexed lookup check-array-result
const item = items[index];
invariant(item !== undefined, 'No item at index %s', index);
handle(item!);Strict indexed-access checking can still require `!` after the call; prefer the typed wrapper for repeated use.
Identify the invariant error recognize-error
try { invariant(config.ready, 'Configuration is not ready'); }
catch (error) {
if (error instanceof Error && error.name === 'Invariant Violation') reportBug(error);
else throw error;
}There is no exported error subclass, so this check depends on 1 mutable name string.
Test synchronous failure text test-failure-message
expect(() => invariant(false, 'Unknown type %s', 'ghost')).toThrow('Unknown type ghost');Assert the error name separately if application code depends on `Invariant Violation`.
Keep the message explicit always-pass-message
invariant(connection !== null, 'Expected an active connection');Version 4.0.2's TypeScript declaration requires the message; do not bypass it with an unsafe cast.
Alternatives
| Package | Registry | Pick it when |
|---|---|---|
| tiny-invariant | npm | Choose it for a public standalone invariant helper with direct-use documentation. |
| invariant | npm | Choose it when compatibility with the older Facebook-style message contract matters. |
| zod | npm | Choose it when unknown external data needs typed parsing and structured errors. |
More utils guides
lru-cache · ajv · type-fest · p-limit · find-up · js-yaml · the whole shelf →
How this guide is made: grounded in the library's documentation, release notes, changelog, and issue history, on a fixed rubric — not a hands-on install of every release. The 50 most-downloaded entries are additionally install-verified in clean containers. Corrections: contact the desk.

