Day 23 - Alert Component Part 2 - Dynamic Rendering of SVG Icons
Table of Contents
- Installation
- Alert Component
- Add Close Button to Alert Component
- Alert List Component
- App Component
- Github Repositories
- Github Pages
- Resources
Component Fundamentals with JavaScript Frameworks
On day 23, I render the alert SVG icons dynamically because dynamic rendering is more maintainable, scalable and efficient.
I create components for the SVG icons and render the appropriate icon component by the alert type.
Framework | Method |
---|---|
Vue 3 | :is attribute with the |
Svelte 5 | Dynamic component is capitalized |
Angular | ngComponentOutlet structural directive enables dynamic rendering of the components |
Create Icon Components
Vue 3 application
Create an icons
directory under src
- InfoIcon.vue
<template>
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" class="h-6 w-6 shrink-0 stroke-current">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"></path>
</svg>
</template>
- SuccessIcon.vue
<template>
<svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6 shrink-0 stroke-current" fill="none" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z" />
</svg>
</template>
- WarningIcon.vue
<template>
<svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6 shrink-0 stroke-current" fill="none" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z" />
</svg>
</template>
- ErrorIcon.vue
<template>
<svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6 shrink-0 stroke-current" fill="none" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10 14l2-2m0 0l2-2m-2 2l-2-2m2 2l2 2m7-2a9 9 0 11-18 0 9 9 0 0118 0z" />
</svg>
</template>
In the .vue
files, the SVG icon is defined in the <template> element.
SvelteKit application
Create an icons
directory under src/lib
- info-icon.svelte
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" class="h-6 w-6 shrink-0 stroke-current">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"></path>
</svg>
- success-icon.svelte
<svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6 shrink-0 stroke-current" fill="none" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z" />
</svg>
- warning-icon.svelte
<svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6 shrink-0 stroke-current" fill="none" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z" />
</svg>
- error-icon.svelte
<svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6 shrink-0 stroke-current" fill="none" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10 14l2-2m0 0l2-2m-2 2l-2-2m2 2l2 2m7-2a9 9 0 11-18 0 9 9 0 0118 0z" />
</svg>
In the .svelte
files, the SVG icon is defined in the file.
Angular 20 application
Create an icons
directory under src/app
- icon.component.ts
@Component({
selector: 'app-info-icon',
template: `
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" class="h-6 w-6 shrink-0 stroke-current">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"></path>
</svg>
`,
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class InfoIconComponent {}
@Component({
selector: 'app-success-icon',
template: `
<svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6 shrink-0 stroke-current" fill="none" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z" />
</svg>
`,
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class SuccessIconComponent {}
@Component({
selector: 'app-warning-icon',
template: `
<svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6 shrink-0 stroke-current" fill="none" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z" />
</svg>
`,
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class WarningIconComponent {}
@Component({
selector: 'app-error-icon',
template: `
<svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6 shrink-0 stroke-current" fill="none" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10 14l2-2m0 0l2-2m-2 2l-2-2m2 2l2 2m7-2a9 9 0 11-18 0 9 9 0 0118 0z" />
</svg>
`,
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class ErrorIconComponent {}
In the component classes, the SVG icon is defined as an inline template because the code is short. It can be seen in the template
property of the @Component
decorator.
Dynamic Rendering of the icon in the Alert Component
The goal is to delete the conditional branches from the Alert
component.
Vue 3 application
<script setup lang="ts">
import InfoIcon from '@/icons/InfoIcon.vue'
import SuccessIcon from '@/icons/SuccessIcon.vue'
import WarningIcon from '@/icons/WarningIcon.vue'
import ErrorIcon from '@/icons/ErrorIcon.vue'
const icon = computed(() => ({
info: InfoIcon,
warning: WarningIcon,
error: ErrorIcon,
success: SuccessIcon,
}[type]))
</script>
Import the Icon components to the Alert
component. Then, define an icon
computed ref that derives the icon to display. The dictionary indexes the type
prop to determine the appropriate icon.
<component :is="icon" />
The v-if
and v-if-else
are replaced with one line of code.
SvelteKit application
<script lang="ts">
import InfoIcon from '$lib/icons/info-icon.svelte'
import ErrorIcon from '$lib/icons/error-icon.svelte'
import SuccessIcon from '$lib/icons/success-icon.svelte'
import WarningIcon from '$lib/icons/warning-icon.svelte'
const Icon = $derived.by(() => ({
info: InfoIcon,
success: SuccessIcon,
warning: WarningIcon,
error: ErrorIcon,
}[alert.type]));
</script>
Import the Icon components to the Alert
component. Then, define an Icon
derived rune that returns the icon to display. The dictionary indexes the alert.type
prop to determine the appropriate icon.
<Icon ></Icon>
The if-else-if control flow is replaced with <Icon></Icon>
. The dynamic component's name must be capitalized; therefore, the derived rune is Icon
.
Angular 20 application
import { NgComponentOutlet } from '@angular/common';
import { ErrorIconComponent, InfoIconComponent, SuccessIconComponent, WarningIconComponent } from '../icons/icon.component';
@Component({
selector: 'app-alert',
imports: [NgComponentOutlet],
template: `
<ng-container [ngComponentOutlet]="icon()" />
`,
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class AlertComponent {
icon = computed(() => {
return {
info: InfoIconComponent,
warning: WarningIconComponent,
error: ErrorIconComponent,
success: SuccessIconComponent,
}[this.type()];
});
}
Import the Icon components to the AlertComponenet
. Then, define an icon
computed signal that derives the icon to display. The dictionary indexes the type
input signal to determine the appropriate icon.
The if-else-if control flow is replaced with <ng-container [ngComponentOutlet]="icon()" />
.
The template is cleaned up to have one line of code to render the dynamic component.
We have successfully rendered the icon component in the alert in Vue, Svelte and Angular frameworks.
Github Repositories
- Vue 3: https://github.com/railsstudent/vue-alert-component
- Svelte 5: https://github.com/railsstudent/svelte-alert-component
- Angular 20: https://github.com/railsstudent/angular-alert-component
Github Pages
- Vue 3: https://railsstudent.github.io/vue-alert-component
- Svelte 5: https://railsstudent.github.io/svelte-alert-component
- Angular 20: https://railsstudent.github.io/angular-alert-component
Resources
- Vue Dynamic Component:https://vuejs.org/guide/essentials/component-basics#dynamic-components
- Svelte 5 Dynamic Component: https://svelte.dev/docs/svelte/v5-migration-guide#svelte:component-is-no-longer-necessary
- Angular NgComponentOutlet: https://angular.dev/api/common/NgComponentOutlet
Top comments (0)