Search code examples
sveltevitecustom-element

What is the correct configuration to create a custom element in a Svelte 4 and vite project?


After having done the following steps

  1. create Svelte project via npm create vite@latest
├── @sveltejs/vite-plugin-svelte@2.4.6
├── svelte@4.2.2
└── vite@4.5.0
  1. Change App.svelte to
<svelte:options customElement="my-element" />

<script>
    export let character = '!'
</script>

<h1>my-element{character}</h1>

<style>
    h1 {
        color: teal;
    }
</style>
  1. Change main.js to
import App from './App.svelte'
export default App
  1. Add the compiler option to vite.config
import {defineConfig} from 'vite'
import {svelte} from '@sveltejs/vite-plugin-svelte'

// https://vitejs.dev/config/
export default defineConfig({
    plugins: [svelte({
        compilerOptions: {
            customElement: true,
        }
    })],
})
  1. Running npm run build

The dist directory contains following files

dist
│   index.html
│   vite.svg
└───assets
    │   index-22b2453f.js

While it seems to be possible to import and use the custom element from the generated .js file I wonder why it's not just one bundle.js file as often described like in this answer

What configuration is neccessary so that one .js file is generated with a given 'static' name without hash where the custom element can be imported from?


Solution

  • I hope I understand correctly your question, to generate a single 'static' bundle, use build.rollupOptions in vite.config.js. You can find more informations there.

    Based on your example, in step #4, vite's config will look:

    import {defineConfig} from 'vite'
    import {svelte} from '@sveltejs/vite-plugin-svelte'
    
    // https://vitejs.dev/config/
    export default defineConfig({
      plugins: [
        svelte({
          compilerOptions: {
            customElement: true,
          },
        }),
      ],
      build: {
        target: "esnext",
        rollupOptions: {
          input: {
            index: "./src/main.js",
          },
          // single
          output: {
            format: "umd",
            chunkFileNames: `[name].[hash].js`,
            entryFileNames: "my-element.umd.js", // <--
            dir: "dist",
          },
        },
      },
    });
    

    Then you can add the file in index.html (file is in public folder):

    <script type="module" src="my-element.umd.js" defer></script>

    Or import it from a Svelte component (file is in src folder):

    import * as MyElement from './my-element.umd.js'

    And then use the custom element:

    <my-element character="⚡" />