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: '-' })
⚠️ 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!"/>
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>
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
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
+ }
+ })
]
})
- ⚠️ 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.
Latest comments (0)