react-intl definemessages duplicate id collision error
Covers react-intl duplicate message ids: find them with the eslint rule, rename to a namespaced convention, verify clean. Use it when the build reports a duplicate id. Not for intentional sharing, extract those to one module.
Fix react-intl defineMessages duplicate id collision
TL;DR
Two messages share the same id, so give every message a unique id or let the extractor generate them. Run the eslint rule that flags duplicates to find every collision at once. The usual cause is copy-pasting a defineMessages block and forgetting to change the id.
The error
Error: Duplicate message id: "app.header.title"
found in src/components/Header.js and src/components/Footer.jsFix it
Step 1: Find every duplicate id in one pass
npx eslint src --rule '{"formatjs/no-duplicate-id": "error"}' | head -20Expected: ESLint lists each duplicated id with both file locations.
Step 2: Rename the ids so each message is unique
grep -rn "app.header.title" src/ | head -10Expected: You see both call sites and can rename one of them to match its component.
Step 3: Use namespaced ids by convention going forward
node -e "console.log('pattern: [page].[component].[purpose], e.g. footer.newsletter.title')"Expected: New messages follow the pattern and collisions stop happening.
Step 4: Re-run lint to confirm zero duplicates
npx eslint src | tail -3Expected: No duplicate-id errors remain.
When to use this
- The build or eslint reports a duplicate message id
- Two components accidentally share one id after copy-paste
When NOT to use this
- The ids are unique but the defaultMessage differs per locale, that is fine and expected
- You want shared ids on purpose, extract them to one shared module instead
Tool and version compatibility
- react-intl v5/v6/v7, eslint-plugin-formatjs
- babel-plugin-formatjs auto-id generation as alternative
Variant phrasings
ids generated by the extractor collide
Two identical defaultMessages in different files got the same hash id. Give one an explicit id to break the tie.
duplicate id across code-split bundles
The id is fine, the extractor just saw both bundles. Scope extraction per bundle or deduplicate at build time.
Why it happens
react-intl keys everything by message id, so two different defaultMessages with one id fight over the same slot in the catalog. Translators see one string, the app renders whichever won, and the other component shows the wrong copy.
Edge cases
- Auto-generated ids change when the defaultMessage changes, pin explicit ids for stable catalogs
- Shared messages should live in one module imported by both components, not duplicated
- The no-multiple-whitespaces rule often fires alongside, fix both while you are in there
Provenance
Resolved from the public thread: https://vectle.com/posts/pst_0dPjRB6zFPhQeJSzUibLvg