Daydreaming about Svelte-like syntax inside render functions

I appreciate being able to use AlpineJS inside my render functions, but often I catch myself wishing I could use Svelte syntax instead. And have the compiler turn the Svelte code to plain JS.

What do you think: how messy would that implementation get?

For example:

defmodule AppWeb.PageLive
  use Surface.LiveView

  def render(assigns) do
    ~F"""
      <div id="page">
        {#if @value and count == 0}
          <SurfaceComponent id="surface-component"/>
        {#else}
          <AnotherSurfaceComponent id="another-surface-component"/>
        {/if}

        <button on:click={handleClick}>
          Clicked {count} {count === 1 ? 'time' : 'times'}
        </button>
      </div>

    <script>
      let count = 0

      function handleClick() {
        count += 1
      }
    </script>
    """
  end
end

I am well aware that there are syntax conflicts right from the get-go between Svelte/JavaScript and Elixir/Phoenix/Surface. I reckon the best way of integrating Svelte is… not doing so. But rather extending Surface functionality into the JavaScript domain, or building a compiler layer on top of Phoenix from scratch – and being ‘inspired’ by Svelte.

Would it be a relieve to have such a tool… or not?

{#if @value and count == 0}

How does the server rendered page get the count value from the front-end?

I’m thinking that the compiler could intervene. The syntax should first be altered, of course, to make it possible for the compiler to discriminate between Elixir variables and JavaScript variables.

For example, the syntax could use % … ; to indicate a JavaScript variable or expression, like: {#if @value and %count == 0;}. Not sure if % is reserved by Elixir or not – but could be another symbol.

A more elaborate example:

defmodule AppWeb.PageLive
  use Surface.LiveView

  def render(assigns) do
    ~F"""
      <div id="page">
        {#if @value and %count == 0;}
          <SurfaceComponent id="surface-component"/>
        {#else}
          <AnotherSurfaceComponent id="another-surface-component"/>
        {/if}

        <button on:click=%handleClick;>
          Clicked %count; %count === 1 ? 'time' : 'times';
        </button>
      </div>

    <script>
      let count = 0

      function handleClick() {
        count += 1
      }
    </script>
    """
  end
end

I can imagine that once you have solved all the possible syntax collisions, you could end up with (hopefully) pleasant syntax to work with. Not convinced myself, tbo, that that assumption is valid, but can’t help wondering.

I think it is writable, but it is just way too much magic to handle
First, you’ll need to precompute expression on elixir (server) side
Second, you’ll need to translate what’s left into Javascript (or write Elixir interpreter in javascript)
Third, you’ll need to make it interconnected back-and-forth with Javascript and Elixir components

This is just way too much magic. If I had a goal to achieve something like this, I would look into web assembly for client side

What I considered, but never managed to try, with svelte would be using svelte client side only and use a custom svelte store to communicate to the backend, which today could possibly even be something like live_json, though plain channels might work as well.

1 Like

This part could be implemented as: only render this here on the server if @value (server) and count == 0 (JavaScript variable passed automatically to backend). But it could also be implemented as: only render this here on the server if @value (server) and if/once it is sent to the client only display/mount if count == 0.

The former is much more magical than the latter, I would say. And also more painful to implement. The latter option still gives you the syntax and the compiled JavaScript.

Interesting. Have an idea what it might be, but what do you have in mind? In general terms how would you utilize WA?