Hey! For those following Hologram’s progress… I’m excited to share that I’ve just published the official roadmap for Hologram. You can check it out at: https://hologram.page/docs/roadmap
The roadmap page provides a comprehensive overview of:
Development Plan: Featuring both immediate priorities (before ElixirConf 2025) and medium-term goals (after ElixirConf), with features listed in planned order of implementation
Feature Status: Detailed breakdown of what’s already implemented and what’s coming next
My immediate focus is on key improvements like optimizing client bitstring performance, implementing component-level rerendering, completing DOM events support, and adding cookies and sessions functionality.
The page also includes detailed status tables for various framework components, including the template engine, framework runtime, and Elixir client runtime features.
I hope this transparency helps the community understand where Hologram is headed and what to expect in the coming months. I welcome your feedback and contributions!
What features are you most excited about? Let me know in the comments!
Non-Command Action Triggers - Enable triggering actions from server code outside of commands.
I guess something like Non-Command Command Triggers - Enable triggering command from server code outside of commands would be done within Component Subscription DSL, right?
I don’t plan to support Vue-like directives at the moment. I think Svelte-like blocks are much more readable and maintainable than Vue-like ones, especially in longer or nested structures. The current approach with {%if condition}...{/if} and {%for item <- items}...{/for} blocks provides a cleaner template structure IMO, but I’m open to discussion of course.
I would like to use both:
Svelte-like blocks for conditionally adding many elements
Vue-like directives for a single element especially if it’s short
This adds 4 extra LOC and 2 extra levels of indentation, so what’s “clear” depends on context. Block conditions are much more helpful to work with multiple elements, but they are too much for a single element.
If you need to colocate multiple components, you can use nested modules:
Creating a module for just one function is overcomplicated. Phoenix supports both live_component/1 and functions components, and they are used in completely different cases (short template vs standalone process with it’s own event handling).
Simply think that some library have tons of components and want to support both Hologram and Phoenix. In fact you require to split 1 or few modules into tens of modules, so each even smallest element like button have it’s own module and the developer have to scroll the documentation to skip tens of entries in the sidebar. This way folks would start to ask ex_doc team to add another tab like MIX TASKS, but for Hologram templates.
Also think about phx_new as right now we need a 1:1 copy with just a bit different template syntax. Does it make sense to copy-paste all of the logic of each generator published on hex? Of course we can ask maintainers to do that for us, but I’m not sure if all of them would agree to double overall template if they even reply.
Support for Phoenix templates
Just think that every project and every library on hex that uses Phoenix templates needs to be rewritten to support Hologram not because the logic changes, but because it uses different syntax or something only different sigil. It’s like Linux vs Windows - sure, Linux have many native games, but it’s still better to optionally support Windows, right?
I believe that such thing would definitely speed up Hologram adoption. Just think that all default Phoenix templates and generators would be supported out-of-the-box by Hologram. For sure it’s not #1 must have, but something I would definitely like to see before 1.0.0 release.
Configuration to minify HTML. The extra spaces may impact on element styles and some selectors to fail:
<div>
{%if some_condition}
What if condition fails? We have extra spaces here!
{/if}
</div>
If remember correctly you have also mentioned some client-side database and sync. I guess it’s a very long term thing, but I would definitely wait for it. It would be nice to add a simple Save cache function and in a simple action tell Hologram to cache templates and sync database, so the client can work offline.
Prop Watchers - Add the ability to react to prop changes with state updates.
What’s the use-case for it? Can’t we just call put_something function call put_state and do something extra?
Unfortunately for my use cases I would need to wait few months for some changes, but the priority list is clear and looks very interesting.
I don’t think so as long as they would be well documented as an alternatives to each other. They are indeed much more readable, but with a good syntax highlight it’s not really a big deal. However they took extra lines and indentation levels which makes them verbose.
On one side Vue-like attributes are better for single elements, but adding same attribute to many elements is bad as same as wrapping elements with this attribute in one parent container… However in opposite Svelte-like blocks are much better for multiple elements, but they are a bit annoying to use on single elements.
For example imagine that you create a menu and every even element have an if (so you cannot group them in one block - again for example). This gives us 2 LOC * number of even elements. In Vue-like syntax I focus on the elements and in Svelte-like syntax I focus on the blocks and both would be fine if they would be used for their purposes.
These are two separate things - in some scenarios we may need to just trigger an action for a single connection (window tab) without having to define a subscription in a component. The DSL would be for channel-like scenarios where we want to send to specific topics (e.g. chat scenario). What are your thoughts in this regard?
I agree with @jam - this would be confusing if we supported both.
I don’t reject the idea of introducing some simple syntax for partials eventually, but not in the medium-term.
Can you elaborate on that?
Browsers collapse sequences of whitespace into a single space when rendering text content. For block elements, whitespace at the beginning and end of content is ignored entirely. In normal text flow, no matter how many spaces, tabs, or line breaks you have between words, they’ll render as a single space. There are specific exceptions to this, but in such rare cases (like <pre> elements) you can just write the if block without surrounding whitespaces.
Minifying whitespace text nodes makes sense, though, in my opinion.
At the moment there is no way to react to prop changes. The only things that can change the state are actions that react to DOM events, other actions, or commands.
Regarding Phoenix template support - I understand that compatibility with Phoenix templates would certainly ease adoption and reduce the migration burden for developers looking to try Hologram. It’s a thoughtful suggestion that I’ve considered carefully.
However, after weighing the tradeoffs, I’ve decided not to pursue Phoenix template compatibility for several important reasons:
Divergent Design Philosophies: Hologram and Phoenix have fundamentally different approaches to UI rendering. Hologram is designed for client-side rendering with Elixir-to-JavaScript transpilation, while Phoenix templates are server-rendered. Supporting both could create conceptual inconsistencies and may confuse developers (point 3).
Maintenance Burden: Supporting two template syntaxes means maintaining two parallel systems, doubling the work for every feature, bug fix, and optimization. This could significantly slow down Hologram’s development.
Confusing Developer Experience: Offering two ways to do the same thing can create decision fatigue and confusion for developers. New users might struggle to understand which approach to use when.
Diluted Innovation: By trying to maintain compatibility with Phoenix templates, we might constrain Hologram’s ability to innovate in ways that are optimized for client-side rendering patterns.
Optimization Challenges: Hologram’s template system is optimized for its specific transpilation approach (and will be optimized even more). Phoenix templates might not transpile as efficiently to JavaScript, potentially leading to performance issues.
Documentation Complexity: Having to document two parallel systems would make the documentation more complex and potentially harder to navigate.
Testing Overhead: We’d need to test all features across both template systems, increasing the testing burden.
Divergent Evolution: As Phoenix continues to evolve its template system, maintaining compatibility would become increasingly difficult over time.
Mixed Paradigms: Phoenix templates are designed with server-side rendering in mind, while Hologram templates are designed for client-side rendering. These different paradigms might lead to subtle bugs or unexpected behaviors when mixing approaches.
Clear Differentiation: Having a distinct template syntax helps clearly position Hologram as a different approach to UI development in Elixir, rather than just an alternative to Phoenix LiveView.
Focus on Core Strengths: By focusing exclusively on own template system, we can ensure it’s the best it can be, rather than dividing resources between two systems.
Migration Path vs. Compatibility: Instead of supporting Phoenix templates directly, we could focus on providing clear migration paths and tools to help developers transition from Phoenix to Hologram.
I believe that maintaining a focused, single-template approach will ultimately lead to a better developer experience and a stronger framework in the long run. That said, I’m committed to providing clear migration paths and documentation to help Phoenix developers transition to Hologram as smoothly as possible.
Regarding the syntax discussion (Svelte vs Vue style), I appreciate your thoughts on this, @Eiji. While Vue’s attribute-based syntax is indeed concise, I believe we should prioritize maintainability and readability over brevity.
Code is written once but read many times by different developers. When quickly scanning through a component to understand its structure and logic, block-style syntax provides clearer visual cues about the control flow:
This approach makes it immediately obvious where loops and conditionals begin and end, especially in complex nested structures. Compare this to the attribute-based approach:
You’re right that Vue-like attributes can be more elegant for single elements, while Svelte-like blocks work better for wrapping multiple elements. However, for Hologram specifically, there are several reasons why a Svelte-inspired block syntax makes more sense in my opinion:
Complex component logic: As Hologram components grow to handle more complex UI logic, the block syntax scales better visually, making the relationship between nested conditions and loops more explicit.
Debugging and maintenance: When troubleshooting components, having explicit opening and closing tags for control structures makes it easier to identify where issues might be occurring.
Learning curve: For developers new to Hologram, block syntax provides clearer visual indicators of the component’s structure, making it easier to understand how the UI is being constructed.
Separation of concerns: Block syntax creates a clearer separation between structural elements and control flow logic, which can lead to more organized and readable components.
Consistency: Supporting both syntaxes simultaneously would likely confuse users and create inconsistency across codebases. Having a single, well-defined approach makes the framework more approachable and reduces cognitive load when switching between projects.
While I acknowledge your point about good syntax highlighting mitigating some readability concerns, the fundamental structure still matters when reviewing code across different environments or when dealing with increasingly complex components.
The block-style approach aligns with Hologram’s goal of creating maintainable, readable UIs that can scale with application complexity while remaining approachable for developers at all experience levels.
Thanks @jam! At this stage, I don’t have detailed specifications for the JS Interop and Component Subscription DSL features - just some initial ideas I’m exploring. Once I get closer to implementing these specific features, I’ll likely open dedicated discussions to gather community input and share my thoughts in more detail.
I appreciate your feedback on the template syntax approach too! Looking forward to sharing more as the project progresses.
Yes, they are different. That’s why I used your naming to name call it Non-Command Command Triggers instead of Non-Command Action Triggers.
defmodule MyLib do
def on_something_asnyc(pid) do
Hologram.trigger_command(pid, :command_name)
end
end
defmodule MyApp.Page do
# …
def init(_params, component, _server) do
pid = self()
spawn(fn ->
# non-prod code - just for example …
Process.sleep(1000)
MyLib.on_something_asnyc(pid)
end)
component
end
def command(:command_name, params, server) do
IO.puts "Triggered from MyLib"
server
end
end
Wait, so Phoenix syntax in heex is confusing too?
<%= if some_condition do %>
<div :if={other_condition}>…</div>
…
<% else %>
…
<% end %>
I’ve used both really often and never found them confusing …
Oh, no need if you don’t plan to support Phoenix templates.
I’m aware that they are seen as single space. I wanted to say that there should be no space at all due to how CSS works. Element with “just one space” is not considered empty.
Selectors 4 now redefines:empty to include elements that contain only whitespace. This was originally proposed as a separate pseudo-class :blank but was recently retconned into :empty after it was determined that it was safe to do so without too many sites depending on the original behavior. Browsers will need to update their implementations of :empty in order to conform to Selectors 4. If you need to support older browsers, you will have to go through the hassle of marking elements containing only whitespace or pruning the whitespace before or after the fact.
There are just many cases where we don’t want extra space (no matter how much) - not even mentioning that templates would be a bit smaller.
Exactly, so if everything happens in action/command then we can do it without any extra API:
defp put_options(component, options) do
value = if component.state.value in options, do: value, else: @default_value
put_state(component, options: options, value: value)
end
Did I missed something? I do not see special watcher for options really necessary if we can just use such a simple function …
Oh, I expected it as simple as:
# hologram clause for render_template/1 goes here …
def render_template(%Phoenix.LiveView.Rendered{} = rendered) do
rendered
|> rendered_to_raw()
|> render_template()
end
# raw i.e. safe HTML clause …
def render_template({:safe, html}) do
# …
end
Does it cause a lot of problem for you?
Oh, not really if you use IO.warn/1 to let user know that templates are converted to raw HTML and to use all Hologram feature developer have to rewrite it later i.e. when there would be a need for it.
…
# raw i.e. safe HTML clause …
def render_template({:safe, html}) do
# use proper message at here
IO.warn "Rendering raw HTLM …"
# …
end
Look at example above. It’s not about supporting all of the live stuff - just render template with it’s assigns as raw HTML.
Maybe I’m wrong with something, but supporting only template (without all live stuff) would be easy to test, no? It’s just about if assigns are part of the raw HTML or not. Again, maybe I see it too plain …
No for a backwards-compatibility i.e. until 2.0.0 version they should not change the Rendered struct.
Yes and that’s what you should let developer know in IO.warn/1 call. The raw HTML would be like a string value added to state.
Oh, I really appreciate your decision, but just sometimes I feel like I’m from another world. People say it’s confusing and I used that in Phoenix and never considered it like you. I just understand all other points except said confusion. If I would say what’s more confusing from my perspective is to not support a short syntax since a mature framework like Phoenix supports both, but maybe it’s just me …
Hmm … For me a good practice is to use :if and :for at the end of the element and if the element is too long then each attribute is on it’s own line. Maybe that’s why I never saw it confusing …
<!-- Looking here: no if/for blocks found -->
<div class="this-element-is-way-too-long-for-a-single-line"
data-key="value"
…
:for={element <- elements}
:if={some_condition}>
<!-- Looking at last attributes: found :if and :for -->
…
</div>
I guess you address something like:
<!-- Looking here: no if/for blocks found -->
<div class="this-element-is-way-too-long-for-a-single-line" data-some-key="some-value" … :for={element <- elements} :if={some_condition}>
<!-- Is that your confusion? -->
…
</div>
I would say it’s rather something that a formatter should worry about, no? Or have I missed something else?
Just for sure - I’m not commenting your decision - I replied only to the confusion part. I clearly don’t see something you do, sorry …
This is completely different story especially that the DSL would in fact render on the server-side. Hologram is supposed to work on everything it could on the client-side. This minimises server usage like in classic SPA with WebSocket connection.
The whole point is to write elixir on the client side no? This syntax reminds me very much of what Lustre is doing in the Gleam community and they managed to get that to compile to js. If elixir works then do end blocks also work, also I said a syntax “like” Temple anyway, not exactly Temple.
Finding a way of composing the UI in some way that’s native to your underlying language (in this case macros and functions) pays dividends and should be prioritized if it’s possible. It makes picking up UI building as simple as learning the language itself, provides autocomplete and ide support, is easy to extend with new features….
I guess if Elixir works on the client why wouldn’t composing your UI out of Elixir functions work?
It does not matter since the DSL is a macro call and the macros are evaluated in compile-time. In theory you can store DSL as String and evaluate it on the client-side, but this is exactly what templates do - just in different format.
As author mentioned he don’t have plans on supporting other formats as he want to focus on just one.
Also do … end block in practice looks very bad as you can’t tell what specific end is doing. It’s also not like that every DSL in Elixir needs to have many nested DSL calls, but in case of templates it’s a completely different story …
The point is you want to replace HTML when the library aims to replace most of the JavaScript with Elixir. That’s also a completely different topic. At first I also liked Temple, but I can’t say a word for multiple end argument and I agree with the overall decision to move to templates. Maybe we could talk about HAML-like proposals, but unfortunately HTML templates could be way too nested and real world examples are not really readable.
This I’ll concede and I agree. But it’s something that can potentially be designed around if the overall feeling of how it’s used is something OP likes. That’s why I asked if something like it would be preferable.
This is a much more constructive response as instead of just dismissing a question for being asked it provides a path forward for more discussion. Like I stated above I’m not saying adopt Temple wholesale.
It’s hard to tell as the docs especially around templates and layouts are incomplete but just looking at the few examples listed the existing template language looks like heex. I’m not sure if EEx.SmartEngine is used in this project for templates but if it is Temple might just work already (haven’t tested it. I read this thread then immediately asked my question). And if it just works already then I think it’s a valid path to consider going forward
It’s not something we can “design”. We cannot change do … end syntax and without it we don’t have a nested DSL. Therefore the only solution left for nested document structure are templates. No matter if we like them or not.
The point is you are mixing solutions. HAML is much more closer to HEEX and Hologram as all of them are templates (String). You have proposed using parts of language and it’s rather only possible to be done in DSL. DSL and templates are completely different topics.
This does not work as you think. As said DSL → macros → compile time. There is no other way to pass compile-time code to the client than storing said code as a String and evaluate it on the client. Compiling such String to JavaScript is also not an option:
First of all it’s template
Secondly unless we would not validate String we can’t even say if it’s valid Elixir code
This makes it just another template - template with lots of end that we agreed are bad
However compiling said code on client means implement complete Elixir parser and evaluator on the client side. The consequences of your proposal are unfortunately overcomplicated. I really like meta-programming, but there is no way it would be considered even in mid-term plans and everything after it is not clear most probably even for the author.
For sure I have suggested Phoenix template support, but only as a workaround to speed up adoption considering existing hex packages. Even in my suggestion this would be a partial support solution, so for real Live-like features the templates would have to be rewritten anyway - just a bit later.
What you propose would be something similar to what I asked for adoption purposes - there would be a raw HTML string send to the client and added to the existing element i.e. the code would not be send to the client and the evaluation would happen on server side. I don’t think temple have lots of hex packages we would like to support and rewrite letter when Hologram-specific features are needed.
Would a syntax LIKE Temple work. Not would Temples EXACT syntax work. I’m not proposing we change how do end work or that we use do end at all. In fact I’ve already agreed with you that I’d also like to get rid of end and by extension do. But would a syntax that looks like html-element keyword-list: “of-attributes”, similar to HAML and Temple, be preferable if possible?
That’s a description for the DSL - especially when you mention macros and functions.
That’s a description for the template.
Trust me there’s a huge difference here. The Elixir code is translated to AST (abstract syntax tree) then to beam files. On the other hand HAML-like templates would only imitate language - it would not be evaluated or verified as an Elixir code. Therefore as any other templates it would not provide autocomplete or IDE support out of the box.
Ok, so it’s not gonna be DSLlike in Temple then we have one template vs another one. As said author does not plan to support multiple template formats and changing template format now is not likely to happen.
It would not give any benefit and only cost a lot of time to implement. You said that it would look like Elixir, but that’s impossible in HAML as we don’t write do … end and if we choose other format as said many end would look bad. So again in best case even if author would agree it would only imitateElixir code.
I like HAML idea, but I’m not sure we can make it better than HOLO or HEEX template and keep Elixir-like syntax at the same time. If you say “Why we can’t just remove do … end” blocks then I would ask what with id? It’s common to use a hash for it, right? But in Elixir syntax it starts a comment. So we would also drop hash … Is it still Elixir-like syntax after such few changes? In my opinion there is no good solution here, but of course I may be wrong …
This is basically what I wanted to get too. I would say that to my original question your answer would be yes you would like a syntax like temple if one were possible. Perhaps we have different aesthetic perspectives but to me haml is a syntax like temple, which is why I was confused when you suggested that, that one is a dsl and one a dumb template language are implementation details. They’re details that support my asking the question as they prove that there are different fundamental implementations for providing such a syntax.
So if you like the direction temple is heading in you could then say “I like these aspects of this temple. They help facilitate X,y,z. I have these constraints given the context, what can I build that gets me closest to X,y,z within my constraints?” It won’t be haml or temple. It will be whatever that solution is.
Another thought about syntax but a different perspective. You might not want to directly tie UI building to HTML at all. I’m unsure as to the scope of your project but if your goal is apps written in Elixir running anywhere you may want to take a leaf out of this weeks Javascript framework, Lynx, by ByteDance. Have a simplified set of tags that can be remapped to platform specific UI at runtime. It’s similar to how LiveView Native’s views works. If you come up with a good solution for marking accessibility you should be able to figure out how to map a generic <text> to <h1>, <p>, etc (perhaps Apple’s accessibility modifiers can be of inspiration)