N
Nuxt4mo ago
RicharDVD

Embeddable components

Hello, I would like to make a component in my app embeddable to other websites. I would like for the interface to look something like this:
<div id="my-custom-component" prop="hi"></div>
<!-- OR -->
<my-custom-component prop="hi"></my-custom-component>

<script src="mydomain.com/js/my-custom-component.js">
<div id="my-custom-component" prop="hi"></div>
<!-- OR -->
<my-custom-component prop="hi"></my-custom-component>

<script src="mydomain.com/js/my-custom-component.js">
It would be great to have this within the same codebase as my nuxt 3 application. What would be the best way to achieve this?
2 Replies
Jouri Jalving
Jouri Jalving3mo ago
Did you ever find out how to do this?
RicharDVD
RicharDVDOP3mo ago
For us it's currently in the backlog but I had a very basic example (i couldn't yet figure out how to get the css working) as follows: I have created a "widgets" in the root level of the project with the following structure: - /widgets -- /components --- test.vue -- main.js -- vite.config.js vite.config.js
import { URL, fileURLToPath } from 'node:url';
import { defineConfig } from 'vite';
import vue from '@vitejs/plugin-vue';

export default defineConfig({
plugins: [
vue(),
],
resolve: {
alias: {
'~': fileURLToPath(new URL('/', import.meta.url)),
},
},
build: {
rollupOptions: {
input: {
widget: fileURLToPath(new URL('./main.js', import.meta.url)),
},
output: {
inlineDynamicImports: false,
entryFileNames: '[name].js',
chunkFileNames: '[name].js',
assetFileNames: '[name].[ext]',
dir: './public/js',
},
},
},
});
import { URL, fileURLToPath } from 'node:url';
import { defineConfig } from 'vite';
import vue from '@vitejs/plugin-vue';

export default defineConfig({
plugins: [
vue(),
],
resolve: {
alias: {
'~': fileURLToPath(new URL('/', import.meta.url)),
},
},
build: {
rollupOptions: {
input: {
widget: fileURLToPath(new URL('./main.js', import.meta.url)),
},
output: {
inlineDynamicImports: false,
entryFileNames: '[name].js',
chunkFileNames: '[name].js',
assetFileNames: '[name].[ext]',
dir: './public/js',
},
},
},
});
main.js
import { createApp } from 'vue';
import TheChip from './components/test.vue';

function bootstrapCalculator(target, attributes) {
const app = createApp(TheChip, attributes);
app.mount(target);
}

customElements.define('countdown-widget', class extends HTMLElement {
async connectedCallback() {
const shadowRoot = this.attachShadow({ mode: 'open' });

const cssLink = document.createElement('link');
cssLink.setAttribute('rel', 'stylesheet');
cssLink.setAttribute('href', '/js/widget.css');

const ss = new CSSStyleSheet();
ss.replaceSync('.text-red-500{color:blue;}');

shadowRoot.adoptedStyleSheets = [ss];

const attributes = {
name: this.getAttribute('name'),
};

bootstrapCalculator(shadowRoot, attributes);
}
});
import { createApp } from 'vue';
import TheChip from './components/test.vue';

function bootstrapCalculator(target, attributes) {
const app = createApp(TheChip, attributes);
app.mount(target);
}

customElements.define('countdown-widget', class extends HTMLElement {
async connectedCallback() {
const shadowRoot = this.attachShadow({ mode: 'open' });

const cssLink = document.createElement('link');
cssLink.setAttribute('rel', 'stylesheet');
cssLink.setAttribute('href', '/js/widget.css');

const ss = new CSSStyleSheet();
ss.replaceSync('.text-red-500{color:blue;}');

shadowRoot.adoptedStyleSheets = [ss];

const attributes = {
name: this.getAttribute('name'),
};

bootstrapCalculator(shadowRoot, attributes);
}
});
test.vue
<script lang="ts" setup>
</script>

<template>
<div class="text-red-500">
Het werkt? {{ name }}
</div>
</template>

<style>
.text-red-500 {
color: red;
}
</style>
<script lang="ts" setup>
</script>

<template>
<div class="text-red-500">
Het werkt? {{ name }}
</div>
</template>

<style>
.text-red-500 {
color: red;
}
</style>
package.json
{
"scripts": {
...
"build:fl-component": "vite build --config widgets/vite.config.js"
}
...
}
{
"scripts": {
...
"build:fl-component": "vite build --config widgets/vite.config.js"
}
...
}
When running the build:fl-component script the outputted js and css will be available inside the public/js folder. You can change this however you'd like in the vite.config.js
Want results from more Discord servers?
Add your server