kuon
I’ve been rethinking how I organize templates in Phoenix apps. The standard approach (at least the scaffold and what I consider standard) is separate .html.heex files, but I’m leaning toward using ~H sigil for everything instead.
Why?
The mixed approach bothers me. You end up with implicit rules that nobody enforces: “small components inline (like core_components.ex), big ones (like a live view or a controller or even a toolbar) in files” - but what’s the threshold? “Reusable stuff gets files” - how reusable? These fuzzy boundaries makes me wonder every time I add something.
With all-~H, you get a predictable module structure. Logic and handlers at the top, all rendering at the bottom (at least that’s how I see it). You can scroll to the end of any LiveView and immediately see what it renders. No jumping between files.
More importantly, you can define function components directly inside your template. Need a small helper? Just write a function right there above the main ~H block. This keeps template-specific helpers colocated without polluting your module’s public interface or creating separate files for tiny pieces.
Cons:
Tooling. LSP features, syntax highlighting, formatters all work better with dedicated .heex files. That’s real friction (or is it?)
The other common argument is “designers need separate files.” But honestly, how many teams actually have non-Elixir people editing templates? And even with .heex files, you still need to understand assigns, components, and Phoenix conventions.
Any story that could help?
Has anyone actually done this at scale? I’m talking “old” (maintained, iterated), multiple devs, real production app. Did the tooling gap become painful? Did you regret it?
Or did anyone try this and switch back to separate files? What broke the camel’s back?
I’m less interested in theoretical arguments and more in “I tried this and here’s what happened.” The ecosystem pushes separate files more (now with embed_templates), but I want to hear from people who’ve actually tested the alternative.
Trending in Discussions
Other Trending Topics
Categories:
Sub Categories:
Forums
Popular Tags
- #ecto
- #liveview
- #troubleshooting
- #learning-elixir
- #library
- #deployment
- #erlang
- #testing
- #genserver
- #mix
- #absinthe
- #remote-other
- #otp
- #plug
- #how-to-question
- #macros
- #postgres
- #elixirconf
- #channels
- #exunit
- #discussion
- #code-sync
- #podcasts
- #javascript
- #onsite
- #dialyzer
- #docker
- #authentication
- #umbrella
- #full-time-contract
- #podcasts-by-brainlid
- #ecto-query
- #elixirconf-us
- #ai
- #blog-post
- #elixir-ls
- #phoenix_html
- #iex
- #graphql
- #genstage
- #websockets
- #supervisor
- #advent-of-code
- #distillery
- #processes
- #api
- #forms
- #elixirconf-eu
- #metaprogramming
- #hex










Showing Posts 1 to 10- Show Best Posts
- Show All (oldest first)
- Show All (newest first)
sodapopcan
I’m all kinds of unqualified to answer this but two things:
Why have a threshold, why not just always embedded? That’s what we do (and what I always do). Our app has six devs, a little over a year old, not launched yet though lots of customer testing (we have other apps). Though, ya one of us uses an IDE not meant for Elixir which does not highlight heex.
I assume you are using classic controllers? It’s definitely the opposite for LiveViews as all examples in the documentation use embedded (other than the part that shows you how to do it the other way, of course, lol). I’ve been using controllers a bunch in a personal project and use embedded because I’ve grown to prefer after using LiveView for so long.
Again, apologies for piping up without the proper qualifications to answer.
garrison
Separate template files are really inherited baggage from Phoenix’s roots as an actual web framework rather than the app framework LiveView has grown into. Unfortunately this is not the only example of “web framework thinking” weighing LiveView’s app capabilities down.
In the JS world the React people figured out pretty quickly that templates are the enemy and must be done away with entirely. They inverted the model and built up the DOM in code rather than by gluing strings together (
React.createElement('div', ...)) and then they built a DSL that made that approach pretty (JSX). Incidentally this approach was directly descended from a PHP framework called XHP. Unfortunately a lot of people do not understand this distinction and mistakenly believe that JSX is a template syntax.HEEx was descended from EEx, which I assume came from ERB (Ruby), which in turn has a lineage back through JSP (Java), ASP, and then probably PHP. I wasn’t there, though, so who knows.
EEx is a templating language, but it does embed real Elixir code (as opposed to a degenerate sublanguage like Jinja). HEEx, by actually parsing and validating the HTML, was a good step towards “JSX nirvana” in that the templates were no longer just strings. And Elixir, being a functional language, actually lends itself more to this approach in that the template is a function and can evaluate like one.
Unfortunately HEEx has since grown a number of “DSL” capabilities (
:ifand:for) which are a complete 180 from this path. Frankly the more I think about it the more I am convinced this was a significant mistake.LiveView, unlike HTMX and Hotwire and others, is actually an app framework - like React. It can be used to build stateful “real” apps which happen to be server-rendered. This is significantly different from web frameworks, which are tools used to glue a bunch of forms together.
App frameworks thrive on components rather than templates and views, which is why LiveView naturally grew them. (Unfortunately I think there may be some denial about the fact that those components need state, but I digress.)
In “app framework” world there is no distinction between “templates” and “code”, there is just code. JSX is just JS with fancy syntax. There is no template.
And so the question of separating the template is fundamentally moot. There should be no template.
This is the source of the dissonance you’re feeling.
garrison
Treesitter handles embedded languages just fine. I actually use
~SHfor my heex (long story) and making that work was just a one-line nvim config change. I think this is a non-issue.The formatter handles HEEx fine (it’s our own tooling after all). Not that I have ever used it or anything.
Probably zero, but even if they did what difference does it make if they edit a
.exfile instead? It’s all the same stuff anyway.(Do not believe his lies, he is fully qualified to answer.)
sodapopcan
LOL, ya that was probably worded wrong, I just I meant I do not meet the criteria that was asked of being a multi-year project as well as I’m not really an LSP user (though I have my own monsterous solutions for the LSP-like features I actually care about).
sodapopcan
I can definitely be an issue when people use non-treesitter compatible editors/IDEs. Of course, they probably really shouldn’t be doing that unless they know how to fix it (I’m in that camp for with Vim and had to add HEEx indentation and highlighting myself) but some people are just really used to a particular editor/IDE and have a really hard time switching.
Eiji
For me this is simple … I use a
~Hsigil for every component, so I use it together with props and slots. The.heexfiles are for a pages where a state may be dynamic and controlled by theLiveView. As far as I know this is a default way you have when generating files usingphxgenerators.kuon
I think this is a good explanation of my feeling. Basically we are not working with templates but with components, even “large ones”.
I guess I should just go with my guts and use
.html.heexfiles less as they often introduce friction in my architecture.kuon
My point is,
.heexfiles, even for a page, introduces some discomfort. Live viewrender()are also components. I think this is the core of what I was feeling weird about.Treating live view like any other component makes everything more consistent.
chrismccord
I almost never use separate heex files fwiw. It’s all the same, so do what feels best for you
We try to strike a balance w/ the generators. The only exception for me is largely static or massive markup “pages” that don’t really make sense to collocate. Don’t overthink it
jswanner
The two frontend devs on my team edit templates themselves