> For the complete documentation index, see [llms.txt](https://docs.wem.io/platform/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.wem.io/platform/tutorials/building-widgets/wemscript/10-script-blocks-es-modules.md).

# 10. Script Block - ES Modules

We have seen that if you are not careful, it can be very easy to overwrite classes and functions. Is there really no way to solve this? Yes! The solution lies in ES Modules. Unfortunately, WEM widgets were built before ES Modules became widely adopted. However, while it may not be perfect, using the building blocks that WEM provides can get you close enough if you know what you are doing. ES Modules is a feature in JavaScript that helps encapsulate code, making it possible to use imports and exports to manage sections of code. Let’s see how this can help us fix naming collisions once and for all!

Let’s imagine we have two separate JavaScript modules, each containing a function with the same name.

```javascript
// hello-en.js

export function setHelloText(element) {
	element.textContent = "Hi there!";
}
```

```javascript
// hello-nl.js

export function setHelloText(element) {
	element.textContent = "Hallo daar!";
}
```

Normally, we would encounter a problem if we embedded these files using `scriptreference`. The first issue is naming collisions, and the second is that the `export` keyword is reserved for use in a module. To use both functions, we need to import them. There are two ways to do this: one is with `<script type="module">`, and the other is with the `import()` function. Unfortunately, while using the `<script>` tag is the easiest method, we cannot use it due to the way the WEM runtime handles loading dynamic scripts. Therefore, we are limited to using `import()`. The challenge with this approach is that it is an asynchronous function. In theory, the widget could be unloaded from the page by the time this function resolves, so we need to account for that as well.

```html
<div id="<?attr OutputId() ?>-en"></div>
<div id="<?attr OutputId() ?>-nl"></div>
<? startupscript ?>
	const enEl = document.getElementById(<?js OutputId() ?> + "-en");
	const nlEl = document.getElementById(<?js OutputId() ?> + "-nl");

	import(<?js FileUrl(@HelloEnJsResource) ?>)
		.then(({ setHelloText }) => {
			if (enEl.parentNode) setHelloText(enEl);
		});

	import(<?js FileUrl(@HelloNlJsResource) ?>)
		.then(({ setHelloText }) => {
			if (nlEl.parentNode) setHelloText(nlEl);
		});
<? end ?>
```

We import our JavaScript module dynamically using the `import()` function. When the script is loaded, the `.then()` function is called, with the first argument being the loaded module. We use destructuring syntax `{ setHelloText }` to extract our function from the module.

Since calling `import()` is asynchronous, there is a possibility that the widget may have already been removed by the time this function finishes loading the module. Therefore, we perform a simple check with `if (enEl.parentNode)`, which verifies that the `parentNode` is not `null`. This ensures that the element is still attached to the DOM and that the widget has not been removed.

#### Note Widget Using ES Modules

If we update our Note widget to use ES modules, we can revert to using `class Note` instead of `class WemAcademyNote`, without worrying about another widget using the same class name. Our updated initialization code now looks like this:

```html
<? startupscript ?>
	const symbol = Symbol.for(<?js OutputId() ?>);
	window[symbol] = "loading";

	import(<?js FileUrl(@NoteJsResource) ?>)
		.then(({ Note }) => {
			if (window[symbol] !== "loading") {
				// unloadscript has been called; ignore initializing the note.
				return;
			}

			const note = new Note();
			window[symbol] = note;
			document.documentElement.append(note.rootEl);
		});
<? end ?>
<? unloadscript ?>
	const symbol = Symbol.for(<?js OutputId() ?>);
	const note = window[symbol];

	// Ignore disposing the note. The module has not been loaded yet, and the note is not initialized.
	if (note !== "loading") {
		note.dispose();
	}
	
	delete window[symbol];
<? end ?>
```

Once again, due to the asynchronous nature of the `import()` function, we need to handle some race conditions. In the `startupscript`, within the `then()` code block, we check `if (window[symbol] !== "loading")`. If this condition is true, we know that `unloadscript` has been called before the module has finished loading. This is because, at the end of `unloadscript`, we `delete window[symbol]`, which sets its value to `undefined`. A similar check in the `unloadscript` is `if (note !== "loading")`. If this condition is true, we know that the module has been loaded before `unloadscript` was called, and the value of `window[symbol]` has been set to a `new Note()`, allowing us to safely call `note.dispose()`.


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.wem.io/platform/tutorials/building-widgets/wemscript/10-script-blocks-es-modules.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
