Uncaught DOMException: Failed to execute 'define' on 'CustomElementRegistry': this name has already been used with this registry
Asked Answered
R

8

28

Uncaught DOMException: Failed to execute 'define' on 'CustomElementRegistry': this name has already been used with this registry at http://127.0.0.1:8000/components/@polymer/polymer/lib/elements/dom-module.js:175:16

Tried deleting node-modules and package-lock and reinstalling did not work.

Romeo answered 16/1, 2019 at 7:0 Comment(2)
If I remember correctly I had a similar error when I included the same element multiple times by using (at least) two different paths (even if the relative paths resolved to the same absolute paths). So you could double check if you're using the same convention when you're importing elements.Bushy
Had some issue in importing files. error is resolved now. ThanksRomeo
A
49

this error is due to a custom element tag-name being registered which is already registered; to fix simply check that an element by this name hasn't already been registered. Here is an example that checks to see if something is already registered using the existing API and if not, registers the given Class (inheriting from/extending HTMLElement--at some point):

customElements.get('the-element') || customElements.define('the-element', HTMLTheElement);

For more on the API see https://developer.mozilla.org/docs/Web/API/CustomElementRegistry

in the original question the package management and related tooling likely has a duplicate path to define the same component, possibly a different version, etc causing the error; checking first before attempting to define the tag and register the element will resolve the problem;

Andresandresen answered 28/8, 2019 at 16:17 Comment(3)
Thumbs up because this is the best way I have seen to do it. If this is needed why do libraries like Polymer not have them? I am trying to avoid this extra code for EVERY componentZounds
Thx @Zounds Polymer I think is end of life at v3. I'm not sure what LitElement (afaik the Polymer successor) does however it seems a straightforward issue to abstract away with a DIY solution--using a base class to extend all in-house components, likely itself an extension of an existing thing like LitElement. This would make maintenance easier long-term as we iterate through long-term software lifecycles.Andresandresen
I am doing it without polymer or lit. I am looking more for the standard way to handle this with just spec web componentsZounds
C
17

Well, this worked for me, with no Typescript warnings,

if (!customElements.get('the-element')) { customElements.define('the-element', HTMLTheElement); }

Hope someone will find this useful.

Cheers.

Crematory answered 21/10, 2019 at 15:37 Comment(2)
Works great. CheersDismount
used that while injecting components as scripts, worked fineMarteena
A
12

It is unwise to use the answers above. You want it to fail! The reason being is that your NPM should be deduping duplicate packages, so the fact that you see a certain component being defined on the custom elements registry more than once is a crucial error that you need to debug why the same component is registered more than once.

How to debug, in short, go to your browser, inspect element, network tab, refresh, figure out which files are both registering the same element. Then check in the initiator to see which files are loading those files. Then you get a way better idea of why your app is not resolving the same import to a single place (your deduped dependency).

One reason why you might face this problem is due to semver. If you have multiple different major versions of the same dependency, NPM cannot just dedupe all of the installations to your root node_modules. How you solve this is up to you. Some people use npm-aliases for their different majors of a dependency, some people implement a plugin in their build-tool to resolve paths to a single installation, etc.

Almira answered 5/10, 2020 at 11:41 Comment(5)
Good point, but I use the above when developing and hot reloading, where browser already has the element registered but code around the customElement.define changed,Juridical
You're not understanding that it's possible to re-call in some cases an import. Further you're answering a question not being asked. While it is helpful to note how to avoid these double-import cases it's a comment on the problem not an answer.Andresandresen
When using HMR, it might make sense to use an HMR solution that actually supports custom elements redefine. modern-web.dev/docs/dev-server/plugins/hmr just to name one example. If you need to apply it to your own HMR solution, you can use something like npmjs.com/package/custom-elements-hmr-polyfill which is more of a quick and dirty solution to redefine your custom element with a new HTMLElement extension class.Almira
@Andresandresen I did answer the original question, go to your network tab and figure out why there are multiple modules loaded that do the same customElements.define. This problem cannot be properly solved with code, it is structural. E.g. you need to run npm dedupe. Or if that doesn't work e.g. you are using multiple major versions of the same component, find another way to resolve to a single installation before it hits the browser, e.g. with a bundler plugin or a dev server plugin.Almira
This answer helped. In my case it was the library @d3fc/d3fc-element, and a simple search for that library in my devtools revealed it being loaded twice (from the node_modules of an npm linked package)Pokeberry
M
7

For people that can't use @jimmonts answer because the issue is in one of their dependencies you can use the following snippet:

This happens for us, because a package we are using defines an element. But this package is used by multiple apps. And these apps, wouldn't you know it, interact. So customElements.define('x-tag', className) gets called multiple times. And the second time it does, it crashes the app.

function safeDecorator(fn) {
  // eslint-disable-next-line func-names
  return function(...args) {
    try {
      return fn.apply(this, args);
    } catch (error) {
      if (
        error instanceof DOMException &&
        error.message.includes('has already been used with this registry')
      ) {
        return false;
      }
      throw error;
    }
  };
}

customElements.define = safeDecorator(customElements.define);
Melanite answered 11/3, 2021 at 13:55 Comment(0)
D
2

I was getting the same error. You may not have the same issue as me but I thought I would drop my solution here just incase someone runs into the same issue in the future.

I had two modules that both imported the same custom element module, one of the was importing Module.js and the other module.js. Now the browser saw this as two separate files because URLs can be case sensitive, except my server saw this as one file because it is not case sensitive (express.js) or at least it was able to resolve the path to the correct file even with the incorrect case. And so the browser saw two "different" modules both defining the same custom element, but when I searched my source code only one file was defining the custom element.

Downstate answered 5/8, 2021 at 0:33 Comment(0)
B
2

I developed a solution, thats overrite the define with a precheck before define. It works fine for me, just ad the 2 lines into your index.js

customElements.defineclone = Object.assign(Object.create(Object.getPrototypeOf(customElements)).define, customElements);
customElements.define = (name, element) => customElements.get(name) || customElements.defineclone(name, element);
Bernadettebernadina answered 7/2, 2023 at 9:26 Comment(0)
E
1

I had this problem and found out that I was calling on my boundle.js file twice. Since I was using Webpack and HtmlWebpackPlugin, HtmlWebpackPlugin added the reference to my boundled file to my index.html file where I had already referenced it by hand.

Eshelman answered 14/11, 2022 at 10:13 Comment(0)
S
0

All of the answers really helped me understand the issue, thank you!

At my organization, we have developed a component library that has been around for a few years, and unfortunately the various interacting projects can't always be on the same versions. So let's say that the WebHeader team uses an old version of the company's <corp-input> component, but some page that has to include the WebHeader needs to use a newer version of the same component?

Our devs have long since implemented a try-catch around the Lit components' customElements.define calls, but this means that the page is stuck with the old version unwil the WebHeader team gets to the upgrade in their backlog.

Well, we engineered a solution: If the team knows that there will be a conflict, instead of using <corp-input>, what if instead they used <corp-input-new>, and told the Lit component to render those tags? This way, the header looks good despite older component versions being loaded AND the page can benefit from the latest version alongside the older ones.

Here's how you do it:

  1. If you try to just call customElements.define('corp-input-new', CorpInput), you'll get a Javascript exception saying that a component can only defined to render one tag name. BUT all you need is a copy of that Component type, which you can get for free by using inheritance! So, first you say class CorpInputNew extends CorpInput { };, and then you say customElements.define('corp-input-new', CorpInputNew);. You'll probably need to do this in your Angular's app.component.ts constructor.

  2. There may be a method inside of your library's Component that is hard-coded to do something undesirable. For instance, I needed to extend CorpSelect, which also means that I needed to extend CorpSelectOption and all the functions that explicitly query on this.querySelectorAll('corp-select-option').

    i. If the method is not read-only, you can just re-implement the method inside of your extended class's curly braces: ... extends CorpInput { update(changedProps: any): { ... } }.

    ii. You probably won't have access to the this keyword (at least, not as it's used in the original Component to select class attributes), so instead try to select on yourself and assign it to something: const thisInput: any = this.querySelectorAll('corp-input-new'); thisInput.someNonPrivateFunction(...);.

    iii. Sometimes there are read-only methods. In Lit, the component may have something like static get selectorItem(): string { return 'corp-select-option,corp-select-group'; }, which cannot be easily overridden like above. Instead, you'll need to do this after your extended class's definition: Object.defineProperties(CorpSelectNew, { selectorItem: { value: 'corp-select-option,corp-select-group', writable: true } });.

  3. Lastly, you could implement a versioning system on your components to more easily identify exactly which version is being used when debugging, since minified component Javascript is a nightmare to read. In your custom component, when you register it, make sure you have a public static attribute like "VERSION". That way, you can always ask your browser which version is registered by writing in the console: customElements.get('corp-input').VERSION.

Saucepan answered 25/8, 2023 at 22:45 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.