> Canonical URL: https://codingcat.dev/post/adding-web-component-for-ionic-modals

# Adding Web Component for Ionic Modals

![CodingCat.dev tutorial cover image](https://cdn.sanity.io/images/hfh83o0w/production/ccbc8203df1de782371c3ee852dbc5f647c83225-1920x1080.png?w=1920&h=1080&q=80&fit=max&auto=format)[![Alex Patterson](https://cdn.sanity.io/images/hfh83o0w/production/a606dfaa381aab08ddbe16a33fe1d3e1fb37c927-400x400.jpg?w=48&h=48&fit=max&auto=format)Alex Patterson](/author/alex-patterson)September 11, 2019

So for this Web Component we are going to code a [Custom Element](https://developer.mozilla.org/en-US/docs/Web/Web_Components/Using_custom_elements) from scratch, not using Stencil, Polymer, or Angular.This just builds a simple search modal that allows for Algolia search searchResults:

![](https://cdn.sanity.io/images/hfh83o0w/production/b1be334c7c7bb64492420c19b26e7608d3086fdf-1236x1248.png?rect=0,277,1236,695&w=1920&h=1080&fit=max&auto=format)

### Modal Code

I don't know if I can add much more than what Eric Bidelman wrote for [Custom Elements](https://developers.google.com/web/fundamentals/web-components/customelements). I would just say you can basically put anything in them, just make sure you have the `connectedCallback()` in place if you need to run code each iteration.

> connectedCallback: Called every time the element is inserted into the DOM. Useful for running setup code, such as fetching resources or rendering. Generally, you should try to delay work until this time.

Search Purr-fectly;
			const ionButtons = document.createElement('ion-buttons');
			ionButtons.setAttribute('slot', 'primary');
			const ionButton = document.createElement('ion-button');
			ionButton.addEventListener('click', async () => {
				const modalController = document.querySelector('ion-modal-controller');
				await modalController.dismiss({ dismissed: true });
			});
			const ionIconClose = document.createElement('ion-icon');
			ionIconClose.setAttribute('slot', 'icon-only');
			ionIconClose.setAttribute('name', 'close');
			ionButton.appendChild(ionIconClose);
			ionButtons.appendChild(ionButton);
			ionToolbar.appendChild(ionButtons);
			ionToolbar.appendChild(ionTitle);
			ionHeader.appendChild(ionToolbar);
			const ionSearchbar = document.createElement('ion-searchbar');
			const searchContent = document.createElement('ion-content');
			ionSearchbar.addEventListener('ionChange', async (ev) => {
				try {
					const searchResults = await index.search({ query: ev.detail.value });
					const ionList = document.createElement('ion-list');
					searchResults.hits.forEach((hit) => {
						const ionItem = document.createElement('ion-item');
						ionItem.setAttribute('href', hit.url);
						const ionLabel = document.createElement('ion-label');
						const title = document.createElement('h2');
						title.innerHTML = hit._highlightResult.title.value;
						ionLabel.appendChild(title);
						const summary = document.createElement('p');
						summary.innerHTML = hit._highlightResult.summary.value;
						ionLabel.appendChild(summary);
						ionItem.appendChild(ionLabel);
						ionList.appendChild(ionItem);
					});
					searchContent.innerHTML = ionList.innerHTML;
				} catch (err) {
					console.log(err);
					console.log(err.debugData);
				}
			});
			this.shadowRoot.appendChild(ionHeader);
			this.shadowRoot.appendChild(ionSearchbar);
			this.shadowRoot.appendChild(searchContent);
		}
	}
);
" aria-label="Copy code">

```
customElements.define(
	'modal-search',
	class extends HTMLElement {
		constructor() {
			super();
			this.attachShadow({ mode: 'open' });
		}
		connectedCallback() {
			const ionHeader = document.createElement('ion-header');
			const ionToolbar = document.createElement('ion-toolbar');
			const ionTitle = document.createElement('ion-title');
			ionTitle.innerHTML = Search Purr-fectly;
			const ionButtons = document.createElement('ion-buttons');
			ionButtons.setAttribute('slot', 'primary');
			const ionButton = document.createElement('ion-button');
			ionButton.addEventListener('click', async () => {
				const modalController = document.querySelector('ion-modal-controller');
				await modalController.dismiss({ dismissed: true });
			});
			const ionIconClose = document.createElement('ion-icon');
			ionIconClose.setAttribute('slot', 'icon-only');
			ionIconClose.setAttribute('name', 'close');
			ionButton.appendChild(ionIconClose);
			ionButtons.appendChild(ionButton);
			ionToolbar.appendChild(ionButtons);
			ionToolbar.appendChild(ionTitle);
			ionHeader.appendChild(ionToolbar);
			const ionSearchbar = document.createElement('ion-searchbar');
			const searchContent = document.createElement('ion-content');
			ionSearchbar.addEventListener('ionChange', async (ev) => {
				try {
					const searchResults = await index.search({ query: ev.detail.value });
					const ionList = document.createElement('ion-list');
					searchResults.hits.forEach((hit) => {
						const ionItem = document.createElement('ion-item');
						ionItem.setAttribute('href', hit.url);
						const ionLabel = document.createElement('ion-label');
						const title = document.createElement('h2');
						title.innerHTML = hit._highlightResult.title.value;
						ionLabel.appendChild(title);
						const summary = document.createElement('p');
						summary.innerHTML = hit._highlightResult.summary.value;
						ionLabel.appendChild(summary);
						ionItem.appendChild(ionLabel);
						ionList.appendChild(ionItem);
					});
					searchContent.innerHTML = ionList.innerHTML;
				} catch (err) {
					console.log(err);
					console.log(err.debugData);
				}
			});
			this.shadowRoot.appendChild(ionHeader);
			this.shadowRoot.appendChild(ionSearchbar);
			this.shadowRoot.appendChild(searchContent);
		}
	}
);
```

### Event Listener for click

This is utilizing [Ionic's Modal](https://ionicframework.com/docs/api/modal). Please also not you *MUST* include the `` in your site or this will not work (trust me lost hours of my life on that one)! Notice in `modalController.create` we are passing a `component: 'modal-search'` property. This is the name of custom element that we created above.

 {
	b.addEventListener('click', async (event) => {
		// initialize controller
		const modalController = document.querySelector('ion-modal-controller');
		await modalController.componentOnReady();
		// present the modal const modalElement =
		await modalController.create({
			animated: true,
			component: 'modal-search',
			componentProps: {
				search: event.currentTarget.getAttribute('search')
			}
		});
		await modalElement.present();
	});
});
" aria-label="Copy code">

```
mainSearch.forEach((b) => {
	b.addEventListener('click', async (event) => {
		// initialize controller
		const modalController = document.querySelector('ion-modal-controller');
		await modalController.componentOnReady();
		// present the modal const modalElement =
		await modalController.create({
			animated: true,
			component: 'modal-search',
			componentProps: {
				search: event.currentTarget.getAttribute('search')
			}
		});
		await modalElement.present();
	});
});
```

## The BEST Part

Now I could package this up and add it to NPM as a nice package. Better yet we could create it from a Stencil component next.Let me know what you think!

## Recent Posts

[View More](/blog/page/1)[![Dockerless Serverless GPUs: How I Built a Fast, Cheap AI SaaS with RunPod Flash](https://cdn.sanity.io/images/hfh83o0w/production/78383c8986667bc1eaa504786488d88f212929a8-1280x720.jpg?w=1920&h=1080&q=80&fit=max&auto=format)](/post/dockerless-serverless-gpus-fast-cheap-ai-saas-with-runpod-flash)

### [Dockerless Serverless GPUs: How I Built a Fast, Cheap AI SaaS with RunPod Flash](/post/dockerless-serverless-gpus-fast-cheap-ai-saas-with-runpod-flash)

October 6, 2026

Hosted image APIs cost 3 to 4 cents per generation. Using RunPod Flash for dockerless serverless GPUs, FlashFun.dev drops that to a fraction of a cent, turning $1 into 500 to 3,000 images. Here is the full architecture, the benchmarks, and the code.

[![Alex Patterson](https://cdn.sanity.io/images/hfh83o0w/production/a606dfaa381aab08ddbe16a33fe1d3e1fb37c927-400x400.jpg?w=48&h=48&fit=max&auto=format)Alex Patterson](/author/alex-patterson)[![Why We Are Absolutely Obsessed with Sticker Mule’s Holographic Stickers](https://cdn.sanity.io/images/hfh83o0w/production/6338342f33d07e920431bb2eaa7a1015b5f6ad45-2752x1536.jpg?rect=11,0,2731,1536&w=1920&h=1080&q=80&fit=max&auto=format)](/post/why-we-are-absolutely-obsessed-with-sticker-mule-s-holographic-stickers)

### [Why We Are Absolutely Obsessed with Sticker Mule’s Holographic Stickers](/post/why-we-are-absolutely-obsessed-with-sticker-mule-s-holographic-stickers)

June 23, 2026

From the first unboxing to daily wear on laptops, bottles, and notebooks, these iridescent, ultra-durable stickers turn every surface into a vivid, futuristic showcase for your brand or art.

[![Alex Patterson](https://cdn.sanity.io/images/hfh83o0w/production/a606dfaa381aab08ddbe16a33fe1d3e1fb37c927-400x400.jpg?w=48&h=48&fit=max&auto=format)Alex Patterson](/author/alex-patterson)