Mount Svelte
Render Svelte Components server-side via rollup.
For performance reasons, compilation only happens when some code has changed.
Although it should be independent, I developed and tested everything on vite_rails
.
Status / Notice
Server-side rendering is the bottleneck in this pipeline. Although rollup is mighty, this gem is in a early state and there sure are limitations. So, please always test your components early if they pass ssr.
I have written everything in ESM syntax.
Although the common-js plugin is installed, I did not test it,
so require
for example may not work in Svelte components.
To check which import statements work, please check the components in the
spec/rails-vite-test-app/app/frontend/javascript/components
gem.
Requirements
- actual node installed on the server
- I tested it all on Rails-7
Installation
gem "mount-svelte"
# and execute
$ rails mount_svelte:install
#=> writes config/mount_svelte.yaml
#=> all configs there
#=> required configs: frontend_folder and components_folder
setup the npm module @csedl/mount-svelte on the client side
Usage
= svelte_component("myComponent", props = { items: ['item1', 'item2'] })
Option render_server_side: :auto
default: true
Works with hotwired/turbo
only
By passing the render_server_side: :auto
option to the view helper,
it checks if the request is an initial request (request header X-Turbo-Request-ID
is not present),
then does nothing server-side but add a tag. Assuming the filename is HelloWorld.svelte
:
= svelte_component("HelloWorld", message: "Property from the server", render_server_side: :auto, class: "box")
#=> output on the rails-console:
Added Svelte-Tag for custom element: «<hello-world message='Property from the server' class='box svelte-component'></hello-world>»
The functionality of the javascript part to create the custom element is not included in this gem and its dependent node-module.
Svelte can be built as a custom element, but that way svelte would add a shadow dom
so that global styles would not be applied to the components.
However, with svelte-tag (i am using this) or its fork svelte-retag
you can build
a custom element without shadow dom.
This works perfectly with hotwired/turbo because the custom elements are built on the
first page load and survive page visits from turbo. If you then visit a page that
has the %hello-world
tag, the component will appear immediately.
This is not a default setting, as it requires more configuration.
Styles
For 99% of use cases you can just skip this chapter.
You can simply work with global styles as well as styles within the svelte component.
A server-side rendered svelte component has 2 states:
Before hydration
- The
svelte_component
view helper renders the styles contained within the component into a style tag within the component's wrapper element. This has to be done this way because of Turbo. - In very, very rare cases, global styles are not applied in the same way as after hydration.
After hydration
- Svelte adds a style tag inside the header
- Svelte renders the component again, which removes the style tag inside the component wrapper.
If you notice a "blink"
For the app to look stable, both states must appear in the same way.
Normally this is the case. But if there are problems,
or you want to see the state before hydration, for development purposes, you can pass
the hydrate: false
option to the view helper,
and no hydration will happen for this component.
Performance
Example from the rails console for a medium complex component
- Compiled MyComponent.svelte.js: 0.411ms
- => happens only once
- Rendered MyComponent.svelte server-side: 0.518ms
- => happens on every request
- Completed 200 OK in 521ms (Views: 520.2ms | ActiveRecord: 0.0ms (0 queries, 0 cached) | GC: 0.3ms)
Testing
Testings are within the gem by rspec
. And there is a rails project with
testings (based on playwright) where some examples are built in.