Skip to content
On this page

If you already author in a framework, it might be possible to use Web Components with small changes to either your exporting process or bundling config. Some of the libraries that are able to both import and export Web Components are:

  • Svelte
  • Vue
  • Preact

Svelte

Svelte leverages a <svelte:options> config to output web components.

html
<svelte:options tag="my-counter" />

<script>
	let count = 0;

	function inc() {
		count++;
	}

	function dec() {
		count--;
	}
</script>

<button on:click={dec}>
	-
</button>
<span>{count}</span>
<button on:click={inc}>
	+
</button>
<svelte:options tag="my-counter" />

<script>
	let count = 0;

	function inc() {
		count++;
	}

	function dec() {
		count--;
	}
</script>

<button on:click={dec}>
	-
</button>
<span>{count}</span>
<button on:click={inc}>
	+
</button>

Vue 3

Vue 3 uses a defineCustomElement wrapper around your existing Vue components to create web components.

js
import { defineCustomElement } from 'vue'
import MyCounter from "./my-counter.ce.vue.js";

customElements.define("my-counter", defineCustomElement(MyCounter));
import { defineCustomElement } from 'vue'
import MyCounter from "./my-counter.ce.vue.js";

customElements.define("my-counter", defineCustomElement(MyCounter));
html
<template>
	<div>
		<button @click="dec">
      -
    </button>
		<span>{{ state.count }}</span>
		<button @click="inc">
      +
    </button>
	</div>
</template>

<script>
	import { reactive } from "vue";

export default {
  setup() {
    const state = reactive({
      count: 0,
    });

    function inc() {
      state.count++;
    }

    function dec() {
      state.count--;
    }

    return {
      state,
      inc,
      dec,
    };
  },
};
</script>
<template>
	<div>
		<button @click="dec">
      -
    </button>
		<span>{{ state.count }}</span>
		<button @click="inc">
      +
    </button>
	</div>
</template>

<script>
	import { reactive } from "vue";

export default {
  setup() {
    const state = reactive({
      count: 0,
    });

    function inc() {
      state.count++;
    }

    function dec() {
      state.count--;
    }

    return {
      state,
      inc,
      dec,
    };
  },
};
</script>

Preact

Preact uses a createCustomElement wrapper around Preact components to output web components.

js
import { createCustomElement } from "@wcd/preact-custom-element";
import { Component, html } from "htm/preact";
import "preact";

class MyCounter extends Component {
  state = {
    count: 0,
  };

  inc = () => {
    this.setState((prev) => ({ count: prev.count + 1 }));
  };

  dec = () => {
    this.setState((prev) => ({ count: prev.count - 1 }));
  };

  render(props, state) {
    return html`
      <button onClick=${this.dec}>
        -
      </button>
      <span>${state.count}</span>
      <button onClick=${this.inc}>
        +
      </button>
    `;
  }
}

customElements.define("my-counter", createCustomElement(MyCounter, ["count"]));
import { createCustomElement } from "@wcd/preact-custom-element";
import { Component, html } from "htm/preact";
import "preact";

class MyCounter extends Component {
  state = {
    count: 0,
  };

  inc = () => {
    this.setState((prev) => ({ count: prev.count + 1 }));
  };

  dec = () => {
    this.setState((prev) => ({ count: prev.count - 1 }));
  };

  render(props, state) {
    return html`
      <button onClick=${this.dec}>
        -
      </button>
      <span>${state.count}</span>
      <button onClick=${this.inc}>
        +
      </button>
    `;
  }
}

customElements.define("my-counter", createCustomElement(MyCounter, ["count"]));

Resources

Licenced MIT