svelte: Support custom element with nested components without errors

I have only 1 customElement and inside it there are several internal components of the Svelte. These internal/nested components cannot be 1 web component.

By setting the customElement option in the rollup, he forced me to put <svelte:options tag={null} /> on all internal components, but that seems to be wrong, as they should not be 1 customElement.

After setting up, I’m getting the following errors for internal components marked as tag={null}:

image

About this issue

  • Original URL
  • State: closed
  • Created 5 years ago
  • Reactions: 31
  • Comments: 22 (2 by maintainers)

Commits related to this issue

Most upvoted comments

Same problem here. It seems that tag={null} does not work as documented in Svelte components imported into a parent component that has a tag="parent-component".

The below does not work

ParentComponent.svelte

<svelte:options tag="parent-component" />
<h1>Parent Component</h1>
<ImportedComponent />

ImportedComponent.svelte

<svelte:options tag={null} />
<h1>Imported Component</h1>
<slot />

However, if I give the imported component a tag name tag="imported-component" and then use the new custom element inside the parent component <imported-component></imported-component> then everything works.

This works, but it’s not what I want

ParentComponent.svelte

<svelte:options tag="parent-component" />
<h1>Parent</h1>
<imported-component></imported-component>

ImportedComponent.svelte

<svelte:options tag="imported-component" />
<h1>Important Component</h1>
<slot />

But, I don’t want to generate all my components as custom elements. I just want the top level element to be generated as a custom element and the rest of my Svelte components to be used within the app as normal Svelte components (not custom elements).

I solved this by using a combination of Rollup and file extensions.

Custom Element components get the extension .wc.svelte. Rollup then sets the customElement config depending on the file extension.

svelte({ customElement: true, include: /\.wc\.svelte$/ }),
svelte({ customElement: false, exclude: /\.wc\.svelte$/ }),

This works smoothly with local and NPM imported components.

Demo: https://jawish.github.io/svelte-customelement-rollup/

Just found this issue after struggling with the same problem. I don’t think that the current all or nothing approach to compiling custom elements is particularly useful. I would find it much more useful if the choice could be made on a per-component basis based on the presence of <svelte:options tag="element-name"/>. Nested components should be normal Svelte components unless they specify otherwise. With this approach, it doesn’t seem that the compiler option would be necessary either.

I want it without having to go through the custom element interface to interact with the imported component. So that I can define one web component built with five Svelte components. The way it works currently is all or nothing if the compiler option for customElements is on. They all six have to be web components defined in global scope. (https://github.com/sveltejs/svelte/issues/3520)

Closed via #8457, to be released in Svelte 4

Thinking about this a bit more, I imagine the main obstacle is how to deal with css. How difficult would it be for a custom element to inline the styles of all child elements until it hits another custom element? For example:

<custom-element-a> // inlines styles from custom-element-a and SvelteComponentA
 <SvelteComponentA>
  <custom-element-b> // inlines styles from custom-element-b and SvelteComponentB
   <SvelteComponentB/>
  </custom-element-b>
 </SvelteComponentA>
</custom-element-a>

There are quite a few open issues related to this e.g. #4274, #3520, #2605.

svelte({
	compilerOptions: {
		dev: !production,
		customElement: true,
		tag: null,
	},
	include: 'src/custom-component/**/*.svelte',
	preprocess: preprocess()
}),
svelte({
	compilerOptions: {
		dev: !production,
		customElement: false,
	},
	include: 'src/component/**/*.svelte',
	preprocess: preprocess()
}),

I tried putting the config as above to use the normal component inside the custom element component. Everything seems fine. However, the css style inside the normal component is not compiled. 😦

I just created a recipe for using Svelte and web components interchangeably in a codebase. And also comes with Storybook and i18n configured. More info: https://github.com/redradix/svelte-custom-element-template

Embedded. I meant inlined in the sense that the styles should be included as a string within the custom element, to be added to the shadow dom. This way the user of the custom element only needs to import a single file.

Maybe this can be of help to someone looking into this issue. I wrote an article on how to export individual svelte components as Web Components while still using Svelte as usual. I hope it can be useful https://www.colorglare.com/svelte-components-as-web-components-b400d1253504

This looks great. Thanks for sharing. I wonder if this functionality could eventually be included as a web component template in the newly announced @sveltejs/kit. What I’d love to see is a Snowpack powered development environment for creating web components that encapsulate Svelte component trees and embed their css (as you have done here).

Okay, now someone explain to me how to do the same thing, but now with tailwind css?

um please fix this

I love svelte, but I spent like an hour figuring out that it doesn’t work like in the docs

at least fix the docs pls

I solved this by using a combination of Rollup and file extensions.

Custom Element components get the extension .wc.svelte. Rollup then sets the customElement config depending on the file extension.

svelte({ customElement: true, include: /\.wc\.svelte$/ }),
svelte({ customElement: false, exclude: /\.wc\.svelte$/ }),

This works smoothly with local and NPM imported components.

Demo: https://jawish.github.io/svelte-customelement-rollup/

If you’re using Svelte with Vite the lines above don’t work, but those below do. It appears that customElement must be nested within compilerOptions.

plugins: [
    ...,
    svelte({ compilerOptions: { customElement: true, }, include: /\.wc\.svelte$/, }),
    svelte({ compilerOptions: { customElement: false, }, exclude: /\.wc\.svelte$/, }),
]