kuon

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.

Showing Posts 1 to 10

sodapopcan

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

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 (:if and :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.

10
Post #2
garrison

garrison

Treesitter handles embedded languages just fine. I actually use ~SH for 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 .ex file instead? It’s all the same stuff anyway.

(Do not believe his lies, he is fully qualified to answer.)

sodapopcan

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

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

Eiji

For me this is simple … I use a ~H sigil for every component, so I use it together with props and slots. The .heex files are for a pages where a state may be dynamic and controlled by the LiveView. As far as I know this is a default way you have when generating files using phx generators. :light_bulb:

kuon

kuon OP

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.heex files less as they often introduce friction in my architecture.

kuon

kuon OP

My point is, .heex files, even for a page, introduces some discomfort. Live view render() 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

chrismccord

Creator of Phoenix

I almost never use separate heex files fwiw. It’s all the same, so do what feels best for you :slight_smile:

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 :slight_smile:

jswanner

jswanner

The two frontend devs on my team edit templates themselves

Where Next? Top

Trending in Discussions Top

AstonJ
As the title says, please share what you’ve been up to with Elixir. Whether that’s been learning it, looking into it, making stuff with i...
2977 94592 917
New
cblavier
Hey there, It’s been more than a year since we started using LiveView as our main UI library and building a whole library of UI componen...
New
heathen
Quite interesting article Google brought me. Didn’t find any mentions about it here. What do you think in general? Would you use togethe...
New
mhanberg
Hi everyone! The first release candidate for the Expert language server project is now available! We’ve published a press release detai...
New
AstonJ
Since we have deprecated our Erlang sections (as we have dedicated Erlang Forums now) let’s add this thread for those who’d like to post ...
New
Null-logic-0
What IDE or editor are you using for Elixir development? Personally, I use Zed, and I really like it, but sometimes I wish there were a ...
New
maennchen
:warning: Security advisory: Decimal DoS vulnerability A vulnerability has been published for decimal where very large exponents can cau...
New

Other Trending Topics Top

JesseHerrick
Hey, I’m Jesse and I’m the main contributor behind Dexter, a full-featured, lightning-fast Elixir LSP optimized for large codebases. It s...
New
marciok
Hi there! We created Gust: A task orchestrator inspired by Airflow. For those who have never heard about Aiflow, it’s a Python-based wor...
New
jimsynz
Beam Bots (or just BB for short) is a framework for building fault-tolerant robotics applications in Elixir using familiar OTP patterns. ...
New
Dmk
Xamal is a deployment tool for Elixir apps that deploys native releases to bare metal servers over SSH. It’s a port of GitHub - basecamp/...
New
webofbits
With AI doing more of the implementation work, I’ve been wondering how much coding I should deliberately keep doing myself. My main conc...
#ai
New
netoum
Corex is an accessible, unstyled UI component library for Phoenix that integrates Zag.js state machines using Vanilla JavaScript and Live...
New

We're in Beta

About us Mission Statement

Options

Thread Display Mode




Thread Preview

Skip Thread Previews