How to Dynamically pass Icon Names

I have a Template which has several child components one of the components take a array as prop.

In that array, I have a field named icon which contains the name of the icon that is to be set…

For example, I have a list and a component that take an array field as shown below

const list = [];

list =[
{
title:"item1",
icon:"boat-outline"
}
{
title:"item1",
icon:"airplane-outline"
}
]

My Problem is how can i set this icon inside my child component.

Attempt 1
i added the

<script src="https://unpkg.com/ionicons@5.4.0/dist/ionicons.js"></script>

to the index.html and used name={{listItem.icon}} in my template… and that did not work, in browser i get the following error

Failed to load module script: The server responded with a non-JavaScript MIME type of “text/html”. Strict MIME type checking is enforced for module scripts per HTML spec.

Attempt 2
Inside my child component i did import "ionicons/icons", and in data i returned a listIcon a second array full of Icon names, and binded them using :icon
this still did not work…

NOTE: i am using Options API and Not Composition

Any Suggestion or pointers

You need to import icons as modules, then reference them in your code.

Here’s an example using methods in the options Api:

import { airplaneOutline, boatOutline } from "ionicons/icons";
...

methods: { 
getIcon(item) {
	let icons = {
		item1: boatOutline, 
		item2: airplaneOutline,
	};

	if (item in icons) return icons[item];
}

}

You can then use the method in your template, probably in a v-for:

	<ion-item v-for="item in list" :key="item.title">
		<ion-icon :icon="getIcon(item.title)" />
	</ion-item>

Didn’t test it, but it should get you started.

@treigh Thank you, I know we can import it as a module and then bind it to the :icon

But my problem is the name of the icon will be passed by the parent component to a child component, inside of which I will be using the icons, so how will I pass that dynamically received icon name to the import statement… that’s just not possible

import { iconName } from "ionicons/icons"; 

Ionicons have an alternative way of using them

<script src="https://unpkg.com/ionicons@5.4.0/dist/ionicons.js"></script>

and then we can use

<ion-icon name="heart"></ion-icon>

in your html, the problem using this 2nd way the script tag needs to be programmatically added to the HTML page and that causes CORS error specifically this one

Failed to load module script: The server responded with a non-JavaScript MIME type of “text/html”. Strict MIME type checking is enforced for module scripts per HTML spec.

i even tried adding “application/javascript” instead of “text/javascript” but that did not work

i did succeed in adding the script tag to head on app mount cycle but havent figured out how to make it work due to the error shown in blockquotes

It would be helpful if you post a snippet of your parent and child components.

Have you tried using a prop to pass the icon value to the child component?

I will do that soon since its a side project and a new week just got started :slight_smile:

@treigh here is that code. & for all those who wish to see the source code, I made a git repo for the same…

Github Link

Basically, I am working on a side project where I want a mixed navigation layout and I have 3 maybe 4 types of layouts that I will need,

In this project for now I plan to use the side menu to navigate between 3-4 SPA’s and each SPA will make use of tabs to navigate between various components and children routes,
There are also some standalone page-level components that serve static info like an About us page where instead of tabs we have a footer.

so the idea is that we build a common base layout and then dynamically build it as per requirement based on that route like adding header Components in the standard layout then header plus tabs component for tab-level navigation… so on so forth…

Have you tried globally importing the icons you want to use? Ionic Vue Quickstart - Ionic Documentation

The problem with passing icon names is it makes it hard for bundlers to know which icons you are actually using in your app, so they end up loading all of the icons. The imports ensure that you only bundle what you are actually going to use. Using global imports will import the icons you want to use upfront, allowing you to dynamically render different icons just by passing in the string name.

I just tried it, but then even with this approach, it needs me to register the icon name inside of main.js… whereas the issue is I don’t know how many pages will be or what the icon name will be until it is passed down to the tabs dynamically…

Here is see 2 potential ways out…

  1. I limit the icons that can be used by setting a dropdown on the page that array will first be created…

  2. I get the <script src> tag to work that way I can just fetch the required icons on the fly…

I would recommend the first approach as it gives you a level of customization while keeping your bundle size small. The problem with the second approach is it requires an internet connection to load the icons in. The reason why we can’t take this second approach with the ionicons npm package is that bundlers like Webpack will not know which icons you intend to use in your app. As a result, these bundlers will end up bundling every single ionicon (over 1000 icons) which will make your bundle size very large.

Just a side note the repo has got 15 unique clones in less than 3 hrs… just wanted to say, show some love if you liked the code… I just happened to look at stats while I was on my way to make it private… won’t hurt anyone if there are a few stars on the repo :slight_smile: :stuck_out_tongue:

<template>
    <ion-icon slot="start" :icon="icon[props.icon]"></ion-icon>
</template>

<script setup>
import * as icon from 'ionicons/icons';

const props = defineProps({
  icon: {
    type: String,
    default: '',
  },
});
</script>

1 Like