Setting Up Phoenix with Inertia.js and Svelte :)

I recently got inertia-phoenix (an Inertia.js adapter for Phoenix) working with Svelte. Setting up the server-side rendering (SSR) with Svelte and esbuild was a little tricky so I made a repo with a detailed guide and example project so that I would remember all the steps for future projects.

Thought I’d share it here in case anyone else is trying to get the same setup going. :wink:

In addition to setting up Phoenix with Inertia and Svelte, the guide also includes a section for setting up several other JS tooling you may want when using Svelte, such as TypeScript, Prettier, ESLint, etc.

I’m definitely not an expert with esbuild setups, so please lmk if you see any mistakes or possible improvements! :pray:

16 Likes

does inertia-phoenix interrupt how live views are rendered?

When using Inertia, we’re rendering views from controllers as dead views so there is no interaction/conflict between Inertia and LiveView pages.

However, it is possible to render a LiveView in a dead view using live_render. And in this case, a potential conflict is if you do page navigation using Inertia. When I’ve tried this in the past, I got an unauthorized live_redirect error from LiveView.

Despite getting the error, the app still functionally works so I’m not sure whether it’s a false error that could somehow be avoided. I haven’t gotten the chance to look into it further though.

1 Like

Thanks for this! Super helpful and worked like a charm.

1 Like

Naive question: why would I pick inertia over liveview?
what would be the rationale?

3 Likes

Inertia.js allows for integration between client-side frameworks (React, Vue, Svelte) and server-focused ones (LiveView) so if you have somewhat complex client-side logic you may want to consider using it. I prefer LiveSvelte though.

If you’re using Svelte within your Phoenix LiveView app you can also call LiveView events as LiveView has really convenient JS interoperability.

2 Likes

I think the answer depends on a lot of things. :sweat_smile:

First, do you have a complex frontend where using a framework like React, Vue, or Svelte would be helpful? If so, you’ll need to decide whether to keep your backend and frontend separate (e.g., Phoenix + Next.js/Nuxt/SvelteKit/Astro) or use a monolithic Phoenix setup.

If you’re sticking with a Phoenix monolith and still want to use a frontend framework, the current options that support SSR (server-side rendering of JS components) that I know of are LiveSvelte, LiveVue, and Inertia.js.

Now, for choosing between LiveSvelte/Vue and Inertia.js: I haven’t used LiveVue, so I can only speak about LiveSvelte and Inertia.js. LiveSvelte is specific to Svelte, while Inertia.js is framework agnostic and can be used with React, Vue, or Svelte. LiveSvelte integrates closely with LiveView, which means it includes all of LiveView’s features, like automatic socket connections and rendering based on socket state changes.

In contrast, Inertia.js doesn’t integrate with LiveView. Instead, it hands off the entire frontend to your chosen frontend framework. It provides SPA-like routing similar to LiveView’s live navigation but without using a socket. For real-time features, you would need to set up your own socket and channels.

I find LiveSvelte offers tighter integration, making the developer experience simpler overall. However, there are some known SSR performance issues and specific challenges (related to making LiveView work in an offline app) that led me to try Inertia.js.

The Elixir Inertia.js adapter has experimental SSR support, and while it sometimes crashes in development, it falls back to non-SSR if it fails. I haven’t encountered SSR issues in non-dev environments. Not integrating with LiveView also avoids some of the offline-related problems I faced.

Both have their pros and cons. I really like LiveSvelte, but Inertia.js solves the specific problems I have better at the moment. That said, I don’t think the issues I have with LiveView and offline functionality are unsolvable—I just haven’t dug into them further since I wanted to try out Inertia.js. Inertia.js isn’t perfect either, but so far, everything seems to just work where it needs to in my current project.

Sorry for the long-winded response! I hope this helps answer your question!

8 Likes

Thank you for your comprehensive answer!

1 Like

Brother, could you make a guide how to implement inertia with pheonix and solidjs (I saw there is a inertia plugin for solidjs)? I have been tinkering on my own but im too dumb to be able to make it work.

1 Like

Here’s a guide that can be useful https://www.youtube.com/watch?v=5t8rTL978Tg

1 Like

Curious, what were the SSR issues and other challenges you were facing? I’m currently using SvelteKit as a SPA paired with Phoenix channels. I’m wondering if LiveSvelte could offer a dramatically simplified DX and perhaps a better UX. It’ll be a PWA and needs to support optimistic UI and some offline capabilities including indexeddb, which led me to put LiveView aside. Are these possible with a LiveSvelte setup? Would be great to hear any insights you have.

Hi @jam — I actually just hacked together a hackathon app this week using SvelteKit + Phoenix Channels, and I think I like it more than using LiveSvelte for PWAs that need to work offline. Specifically, I used SvelteKit in SPA mode. During development, I set up a Vite proxy to talk to Phoenix. In production, the SPA is compiled into Phoenix’s static folder, so I only need to run the Phoenix server — no separate SvelteKit server required.

There are some tradeoffs with this approach, though. Mainly, there’s no SSR unless you prerender pages. But for a PWA with a service worker caching files for offline use, that’s not a big deal — and having the SvelteKit router is great.

You can make LiveSvelte work as an offline PWA, but there are a couple of downsides:

  1. Lack of client-side routing: Liveview has live navigation, but that only works when you’re online. If you want offline routing without page reload, you’ll need to build your own or use a third-party library, neither of which will likely be as full-featured as SvelteKit’s.
  2. CSRF token issues: For offline apps, you’ll need to cache your HTML, which ends up caching the CSRF token used for the LiveView socket connection. This makes reconnecting after being offline for a while tricky with LiveSvelte. I had a very hacky workaround in a previous app, but I wouldn’t recommend it.

In summary, I wouldn’t recommend LiveSvelte for PWAs that need offline capabilities. It’s possible, but right now I don’t think it’s as clean as just using channels.

5 Likes

Interesting, thanks for the insight!

Nice! I used the same for dev. Would be curious to learn how you went about prod. Did you make a mix task for it?

I’ve been experimenting with some Svelte code to make working with Phoenix channels really easy and set up svelte stores that have ecto-like querying and will be eventually synced into indexeddb. Here’s a quick look at the syntax:

// in a /lib file
import { Repo } from "$lib/repo.svelte.js"

export const Messages = new Repo("messages")
// in a .svelte file
import { subscribe } from "$lib/subscribe.svelte.js"
import { Messages } from "$lib/repos/messages.js"

// pass room_id in via props or just use page.params

subscribe(() => `room:${room_id}`) // will be reactive for room_id changing, subscribe also accepts a string for joining the channel in say a SvelteKit load function for prefetch

const messages = $derived(Messages.where(m => m.room_id === room_id).all())

Messages.create(…)
// also has .update and .delete
// can also define a custom method if needed

If you have any interest in playing around with it or collaborating, let me know.

Nice! I used the same for dev. Would be curious to learn how you went about prod. Did you make a mix task for it?

It was a super quick hack project, so I didn’t put much work into the deployment process. I literally just ran vite build and pointed the output into Phoenix’s static folder.

If you’re curious, the code is available here: https://github.com/allthingsweb-dev/allthingsweb/tree/main/hackathon-app

Again, this was a rushed hackathon project, so I took a lot of shortcuts to get it done and deployed quickly. Definitely not a production-grade implementation to follow!

I’ve been experimenting with some Svelte code to make working with Phoenix channels really easy and set up svelte stores that have ecto-like querying and will be eventually synced into indexeddb.

Thanks for sharing — that’s an interesting approach! I think next on my list of experiments is to play around with ElectricSQL and see how it plays with a Phoenix + SvelteKit setup :slight_smile:

1 Like

Nice! I’ve been following it closely. Hope to find some time to experiment. Would be curious to hear how it goes for you. It mostly sounds great but I wonder what gotchas there are. The one I’m aware of is currently no real support for joins. There is some stuff in the works but it appears to be client side only. Beyond that I think there’s quite a few unknowns that will probably only be discovered with more production use.

2 Likes