DEV Community

Christian Sedlmair
Christian Sedlmair

Posted on • Updated on

 

Setup Svelte as Custom Element on Vite

Overview

On every App we have at least one Feature that is special and needs individual frontend logic. This is the point where Svelte's making so much joy - for us and our customers.

Aim is creating Svelte components as reusable Custom Elements for integrating on views or inside other Svelte components which easy can be styled like all other elements.

Why Svelte as Custom elements?

Why working with Custom Elements / Web-Components? Rich Harris don't like them. Reason is Hotwired/Turbo and the initialization of components. Initializig the Svelte Component in a regular element is only possible when the regarding element is present in the current view! Then you would see always a unpleasant «blink»: While the html from the backend is rendered instantly, the Svelte Component needs some milliseconds for building. That would look quite bad. Initializating of Web-Components runs only on very first load and independent from current viewport. Rails-Turbo then, on changing pages, by default, replaces just the body-tag and the Custom-Element survives! By Visiting a page where such a element is used, its just present. That works really fine.

Svelte itself has a option for building custom elements, but, they, build a shadow DOM which means, global styles are not applied! There's a open issue from 2018. So we are using npm/svelte-tag which sovles that.

Why Svelte?

Why React or Vue?

Svelte delivers the same functionality like React or Vue, but without virtual DOM and is faster.

No more document.getElementById(..).addEventListener(..). Just having JS and html in one File together, and it all bound in a handy custom element that can be used everywhere and accessed with params.

Svelte, 2021, was the most loved frontend framework on Stackoverflow.

For understanding the idea of Svelte, look at the enjoyable Presentation rethinking reactivity of Rich Harris, creator of Svelte and Rollup.

A reason for using React could be that its more spread and there are more libraries. Otherwise the young Svelte has a vibrant community and is growing fast.

Example Code

application.js

import { initSvelte } from "vite-svelte-initializer";

const apps = import.meta.glob('../javascript/components/**/*.svelte', { eager: true })
initSvelte(apps, 2, { debug: true, exclude: ['components'], folderSeparator: '-' })
Enter fullscreen mode Exit fullscreen mode

⚠️ vite-svelte-initializer uses use svelte-tag because the default Integration for Custom Elements would generate a shadow-dom so that globally styles wouldn't be applied to the component.

Options for naming see on vite-svelte-initializer

inside any view

<hello-svelte title="Hello Svelte!"/>
Enter fullscreen mode Exit fullscreen mode

frontend/components/hello-svelte.svelte

<script>
    export let title
    let input_value
    let items = ['one', 'two', 'three']

    let handle_input = () => {
        console.log(`input value has changed to "${input_value}"`)
    }
</script>
<template>
    <h1>title: {title}</h1>
    <input on:keyup={handle_input} bind:value={input_value}>
    <ul>
        {#each items as item}
            <li>{item}</li>
        {/each}
    </ul>
</template>
Enter fullscreen mode Exit fullscreen mode

RubyMine, by example, has a well working Svelte Plugin.

We use Svelte for building single components. For Building whole pages or whole frontend with Svelte and Inertia, check Tutorial Stefan Buhrmester, setup Svelte with Inertia on Vite/Rails.

Setup

together with svelte we setup axios

$ npm i svelte @sveltejs/vite-plugin-svelte vite-svelte-initializer axios svelte-preprocess
Enter fullscreen mode Exit fullscreen mode

vite.config.js

  import { defineConfig } from 'vite'
  import RubyPlugin from 'vite-plugin-ruby'
+ import { svelte } from '@sveltejs/vite-plugin-svelte';
+ import sveltePreprocess from 'svelte-preprocess';

  export default defineConfig({
+   resolve: {
+     dedupe: ['axios']
+   },
    plugins: [
      RubyPlugin(),
+     svelte({
+       preprocess: sveltePreprocess(),
+       experimental: {
+         prebundleSvelteLibraries: true
+       }
+     })
    ]
  })
Enter fullscreen mode Exit fullscreen mode
  • ⚠️ sveltePreprocess is necessary for rendering well in a Custom Element. Otherwise it generates the Content but within a #document-root and not visible on the browser. That's strange, did'nt figure out the reason for this behaviour yet.
  • The experimental/prebundle handles a current bug in svelte.

Restart server.

That's it!

Example Code, from above, should work.

Overview

Top comments (0)

🌚 Friends don't let friends browse without dark mode.

Sorry, it's true.