3
votes

I have doubts about how to create a Custom element API. I have followed the documentation, but I get the following warning:

The 'tag' option is used when generating a custom element. Did you forget the 'customElement: true' compile option? (Link.svelte: 1:16)

REPL

I have marked customElement: true in compiler options.

I'm lost... can anyone help me?

1
Are you just seeing this in the REPL, or locally as well?Rich Harris
In both. Although I solved the problem by including <svelte:options tag="my-app" /> in App.svelte.Daniel Alba

1 Answers

24
votes

So it appears that the REPL doesn't really support custom elements. It kind of make sense...

The general idea with custom elements in Svelte is as follow:

1. You set Svelte compile option customElement: true

For example, in the default Rollup template, you'd set this option here in the Svelte plugin for Rollup:

  plugins: [
    svelte({
      compilerOptions: {
        customElement: true,
        ...
      }
      ...
    }),
    ...
  ]

2. You add <svelte:options tag="what-ever" /> to your Svelte components

<svelte:options tag="what-ever" />

<script>
  // make sure component Foo is available, but we don't import
  // it... we'll use it with it's tag <my-foo /> (see bellow)
  import './Foo.svelte'
  export let name = 'World'
</script>

<p>Hello, {name}!</p>

<my-foo {name} />

<style>
 p { color: skyblue; }
</style>

Foo.svelte

<svelte:options tag="my-foo" />

<script>
  export let name
</script>

<p>I am {name}</p>

Note that you can't mix and match custom element components with normal components.

This means you'll have to add the tag option to all you children components and, instead of importing components as usual, you'll use their tag name directly in your markup.

You'll still need to import the files for the components to be available (but with empty imports, eg import './Foo.svelte').

3. You compile / bundle your components however you want.

If we keep with the example of Svelte official template for Rollup, then you'll end up with a generated bundle.js file.

4. You include this bundle.js file in a HTML page:

<script defer src='/build/bundle.js'></script>

5. And now, in this page, you can use the tags you've defined above as if they were native HTML elements, the like of <div> or <strong>.

They will be implemented as Svelte components but, as this stage, this is now an implementation detail.

So, for example, in this page, you can now do things like:

document.body.querySelector('#container').innerHTML = '<what-ever>Foo</what-ever>'

Or, you could have this in your HTML (for example, in the index.html file of the default template):

<body>
  <what-ever>foo</what-ever>
</body>

And that's it. In some situation, this can be fancy, or very handy. You can have custom elements implemented in a smooth declarative framework, that you can use in any context (as long as you can import a .js file), with minimal overhead due to the framework itself.

That's nice if you want to include this as a widget on a Wordpress blog instead of using jQuery, or even if you want to publish a framework-agnostic component.

Now, if your whole app is going to be Svelte based, it loses a lot of its salt, because Svelte custom elements do suffer some limitations (end of the linked section) as compared to regular Svelte components.

Whether you should use them or not depends on your use case.

And, in any event, it seems that you can't play with them in the REPL...

A last thing...

Since I've used the default Svelte template all along this answer, it should be noted that if you want to use it with custom elements, you should not initialize the App component yourself in main.js:

import App from './App.svelte';

// NOT needed with custom elements
//const app = new App({
//  target: document.body,
//  props: {
//      name: 'world'
//  }
//});

export default app;

The entry points will now be individual custom elements that you create with the aforementioned techniques.

BUT you should still ensure that all of your custom elements .svelte files are imported by the entry point (main.js in this case) that is fed to Rollup, or they won't be available in the final bundle.js file that is produced. (Importing in files that are imported by main.js is fine.)