Ressources π
Before starting I want to acknowledge redradix and Andres Martin, who made the hard work for me in this template https://github.com/redradix/svelte-custom-element-template...
If you are in a hurry, you can directly go to have a look at the code here and play with it: https://github.com/stefanonepa/svelte-component-ts
Why? π€
As explained in the github repo redradix/svelte-custom-element-template:
Building custom elements with Svelte is really easy but have a lot of limitations, in this template I'm trying to show the way I solve most of these limitations.
Svelte current limitations:
They solved a very simple use case which was how to wrap a svelte app inside a web component.
How? π
How can we achieve this miracle (hacks inside):
- Build the entry component as a web component
 - Build the sub component as svelte app
 - Inject the css of the sub components in the shadowRoot element
 - If transition are used replace the injection in the document into the shadow element
 
1. Build the shadowRoot wrapper web component
// rollup.config.js
svelte({
  preprocess: sveltePreprocess({ sourceMap: !production }),
  compilerOptions: {
    dev: !production,
    customElement: true,
  },
  emitCss: false,
  include: './src/ShadowRoot.svelte',
}),
2. Build svelte to be injected in the web component wrapper
// rolup.config.js
svelte({
  preprocess: sveltePreprocess({ sourceMap: !production }),
  compilerOptions: {
    dev: !production,
  },
  emitCss: true,
  exclude: './src/ShadowRoot.svelte',
}),
3. inject the generated css into the shadowRoot node
To catch the generated css I modified rollup-plugin-css-only locally to push the generated css on each changes (rebuild)
// ./.rollup/css-only.js
...
generateBundle: function generateBundle(opts, bundle) {
  // Combine all stylesheets, respecting import order
  var css = '';
  for (var x = 0; x < order.length; x++) {
     var id = order[x];
     css += styles[id] || '';
  }
   // Emit styles through callback
   if (typeof options.output === 'function') {
     options.output(css, styles, bundle);
     return;
}
...
Then inject the css right into the bundle (π± Hack alert!) with one important caveat which is that the wrapper web component has to have a style set π₯.
import css from './.rollup/css-only'; 
// rollup.config.js
css({
  output(styles, styleNodes, bundle) {
    const match = production
     ? `.shadowRoot.innerHTML="`
     : `.shadowRoot.innerHTML = "`;
      const currentBundle = bundle[bundleFile];
      currentBundle.code = currentBundle.code.replace(
        match, `${match}<style>${styles}</style>`);
  },
}),
4. Include svelte transition if used into the shadow dom
Svelte gives use some very nice utilities like transition (cf. https://svelte.dev/tutorial/transition)
For my actual understanding is that svelte will inject dynamically computed styles into the head/document, and this won't allow the transition to apply into the shadow dom. That's why we need to replace the document injection by the shadow dom node.
// rollup.config.js
replace({
  '.ownerDocument': '.getRootNode()',
  delimiters: ['', ''],
}),
replace({
  '.head.appendChild': '.appendChild',
  delimiters: ['', ''],
}),
Result π
We have a web component that wraps a svelte app and supports typescript and scss out of the box, with a DX (developer experience) that allows you to change the code and rebuild it automatically.
Svelte-component-ts template π
this template enables svelte to be used with a shadow DOM entry component and then sub component using the goodness of svelte.
This template has stealen inspiration (hacks) from https://github.com/redradix/svelte-custom-element-template thanks to https://github.com/MonkeyAndres
This template includes:
- typescript support out of the box
 - sass support
 - babel with a minimal configuration (cf. rollup.config.js)
 
Recommended tools
Usage
Clone it with degit:
npx degit stefanonepa/svelte-component-ts my-new-component
cd my-new-component
yarn
yarn dev
Constraints
- setup a style in the entry element 
ShadowRoot.svelte. - 
β οΈ Styles in the root component are not scoped by svelte, then choose carefully your selectors if you use some thereβ οΈ . 
Why?
(from redradix/svelte-custom-element-template βοΈ)
Building custom elements with Svelte is really easy but have a lot of limitations, is this template I'm trying to show the way I solve most of these limitations.
Svelte current limitations:
TODO π
[ ] support hot reload
Conclusion
I hope this will help everyone trying to create custom element using all the goodness provided by svelte. I would love to find something less hacky provided by the svelte contributors. But I am still very happy of the result.
Feel free to share your experiences with web components and svelte, ideas for improvement or just say hi π
              
    
Top comments (1)
For future readers. Took me a while to figure out but this is now built into svelte. Please have a look at; github.com/sveltejs/svelte/issues/.... Just set the target element to the shadow root (don't forget to disable emitCss).