I create boilerplate to demonstrate how to setup Phoenix LiveView project with typescript, svelte and tailwind

I have struggled to setup phoenix liveview project with svelte and typescript, tailwind.

Most steps are not very difficult but it was difficult to how to properly load svelte component properly. Some of suggestions I found on elixir forum was not working with different situation. So I created a hook to load svelte component properly and it might work to other frontend library such as react or vue.js.

I’m not sure somebody already solved this issue but it might be good starting point for someone who don’t understand yet. You can find Phovelte in github.

It works so far in my use case, but still need to figure out how to change props dynamically from phoenix liveview. Please leave a comment if there’s better way.

Thanks for reading.

5 Likes

Would be good if you could add to the README an example of using your project, otherwise it just looks like one more playground repo :wink:

2 Likes

:+1: Updated some documents for better understanding. Thanks for the advice. :smiley:

You can instantiate a svelte component from liveview by using hook all right; how do you cleanly remove the component?

I’d just use a custom component:

That’s good point.

I updated hook to support instance clearing by using SvelteComponent.$destroy.
It is probably internal API, but it works for now.

import type { SvelteComponent } from 'svelte';

const hooks = {
  'svelte-component': {
    mounted(this: { el: HTMLElement, _instance: SvelteComponent | null }) {
      const componentName = this.el.getAttribute('data-name');
      if (!componentName) {
        throw new Error('Component name must be provided');
      }

      const requiredApp = require(`./${componentName}.svelte`);
      if (!requiredApp) {
        throw new Error(`Unable to find ${componentName} component`);
      }

      const props = this.el.getAttribute('data-props');
      const parsedProps = props ? JSON.parse(props) : {};

      this._instance = new requiredApp.default({
        target: this.el,
        props: parsedProps,
      });
    },

    destroyed() {
      this._instance?.$destroy();
    }
  },
}

export default hooks;