Frameworks
The examples that import shape-aware-morph use the npm package. It is in progress. See Install.
<morph-icon> is a custom element, so it works in any framework. Define the element once, then use the tag.
Plain HTML
Use the script tag. Set the name attribute, or the name property, or call a method.
<script type="module" src="https://shapcn.com/morph-icon.js"></script>
<button id="toggle" type="button" aria-label="Toggle microphone">
<morph-icon name="mic" size="32" duration="420"></morph-icon>
</button>
<script type="module">
const icon = document.querySelector('morph-icon');
let on = true;
document.getElementById('toggle').addEventListener('click', () => {
on = !on;
icon.name = on ? 'mic' : 'mic-off'; // same as setAttribute('name', ...)
});
await icon.morphTo('mic-off'); // methods return a Promise
</script>
React
Define the element once, then use the tag. Use string or number values for the attributes.
import { useEffect, useState } from 'react';
import { registerLucide, defineMorphIcon } from 'shape-aware-morph';
export function MicButton() {
const [on, setOn] = useState(true);
useEffect(() => {
registerLucide().then(() => defineMorphIcon());
}, []);
return (
<button onClick={() => setOn(!on)}>
<morph-icon name={on ? 'mic' : 'mic-off'} duration="420" />
</button>
);
}
Call defineMorphIcon in an effect. On the server there is no DOM, and defineMorphIcon returns null.
React types
The package types add morph-icon to JSX.IntrinsicElements, to the react module, and to HTMLElementTagNameMap. Any import of shape-aware-morph brings them in. The attributes are typed as name, size, color, stroke-width, duration, easing, stagger, effect, and hover, plus class, className, style, id, key, and ref.
To call the methods, type the ref with MorphIconElement.
import { useRef } from 'react';
import type { MorphIconElement } from 'shape-aware-morph';
export function Hide() {
const ref = useRef<MorphIconElement>(null);
return (
<>
<morph-icon ref={ref} name="heart" />
<button onClick={() => ref.current?.hide()}>Hide</button>
</>
);
}
Vue
Tell Vue that morph-icon is a custom element. Then use the tag.
// vite.config.ts
import vue from '@vitejs/plugin-vue';
export default {
plugins: [
vue({ template: { compilerOptions: { isCustomElement: (tag) => tag === 'morph-icon' } } }),
],
};
<script setup>
import { ref, onMounted } from 'vue';
import { registerLucide, defineMorphIcon } from 'shape-aware-morph';
const on = ref(true);
onMounted(async () => {
await registerLucide();
defineMorphIcon();
});
</script>
<template>
<button @click="on = !on">
<morph-icon :name="on ? 'mic' : 'mic-off'" duration="420" />
</button>
</template>
Svelte
Svelte passes the tag through. Define the element when the component mounts.
<script>
import { onMount } from 'svelte';
import { registerLucide, defineMorphIcon } from 'shape-aware-morph';
let on = true;
onMount(async () => {
await registerLucide();
defineMorphIcon();
});
</script>
<button on:click={() => (on = !on)}>
<morph-icon name={on ? 'mic' : 'mic-off'} duration="420"></morph-icon>
</button>
Custom element rules
- The element reads its attributes when it connects. It then watches the nine attributes in Element.
- The element keeps its state if you move it in the DOM. It stops any running frame when it disconnects.
- Define the element before or after you add the tag. A tag that exists before the definition upgrades with its attributes.