bartblast

bartblast

Creator of Hologram

Hologram - full stack isomorphic Elixir web framework

Hey there! There’s a new kid on the block! :smiley:

What is it?

Full stack isomorphic Elixir web framework that can be used on top of Phoenix.

Inspired by

Elm, Phoenix LiveView, Surface, Svelte, Vue.js, Mint, Ruby on Rails.

How it works

To build your web app, you use basic Hologram blocks: pages, layouts and components.

Hologram builds a call graph of your code (which must follow some basic conventions) and determines which code is used on the client, and it then transpiles such code to JavaScript.

Because the state is kept on the client it makes the programming model simpler, and thanks to stateless or stateful components the app is easily scalable.

Code that is run on the client is encapsulated in “actions”, and code that runs on the server is encapsulated in “commands”. Actions can trigger commands, commands can trigger actions. Both actions and commands can be triggered directly by DOM events.

Client communicates with server through websockets - there is no boilerplate code to make it work, Hologram figures this out automatically.

What is already done

This is work in progress (although usable and used in production). To check what works and what is planned - take a look at the roadmap in the readme: GitHub - bartblast/hologram: Full stack Elixir web framework that intelligently compiles Elixir client-side code to JavaScript

History / background

I tried to write this kind of framework first in Ruby, and I actually managed to create a working prototype, but the performance was not satisfactory. Then I tried Crystal, but it was very hard to work with its AST. Then I moved to Kotlin, but I realised that it’s better to use a dynamically typed language for that… Then I found Elixir in 2018 and fell in love with it. I started to work on Hologram in the summer of 2020.

Is it used in production?

Yes, it is used in Segmetric: https://www.segmetric.com/ Take a look at the “join beta” and “contact pages" which showcase form handling, or check the mobile menu. This all works on transpiled Elixir code! (But please, submit the forms only if you actually want to join the Segmetric beta or contact Segmetric - it is a live page, thanks!)

I want to see some code!

To see how Hologram app is structured, and see some actual code, take a look at the Hologram’s E2E modules: https://github.com/segmetric/hologram/tree/master/e2e

Basic example

defmodule MyPage do
  use Hologram.Page

  route "/my-page-path"

  def init do 
    %{
      count: 0
    }
  end

  def template do
    ~H"""
    <div>Count is {@count}</div>
    <button on:click={:increment, by: 3}>Increment by</button>
    <Link to={MyOtherPage}>Go to other page</Link>
    """
  end

  def action(:increment, params, state) do
    put(state, :count, state.count + params.by)
  end

  def command(:save_to_db, _params) do
    # Repo.update (…)
    :counter_saved
  end
end

# Action can return a new state, or a tuple {new_state, command, params}
# Command can return an action or a tuple {action, params}
# (This is similar to Elm architecture.)

I’m in the process of creating a basic website with installation guide. Let me know what you think about the project? What do you need to try it out? I will be glad to answer your questions :slight_smile:

Most Liked

bartblast

bartblast

Creator of Hologram

@Twfo326 Thanks! Here are the main selling points:

  • State on the client - and all of the problems that get solved by this approach (below)…

  • No latency issues as most of the code is run immediately on the client. This makes it possible to create rich UI or even games. At the moment with LiveView you need something like fly.io to make it bearable, but you still have latency and can’t guarantee the response time (there is always some variance). And you still need some JS or Alpine to make it work. Until someone manages to create quantum internet (e.g. by taking advantage of entanglement), there are no workarounds for this problem. Not sure if this is even technically possible, though :wink:

  • Better offline support (internet connection loss, poor signal, etc.). Since most of the code is run on the client and you only hit the server to run some command from time to time, Hologram can work offline most of the time. This would also make it possible to create PWA’s or mobile apps through WebView, assuming you use something like LocalStorage.

  • Less server RAM used - state is kept in the browser instead of the socket.

  • Less CPU used - most of the code is run by the browser not by the server.

  • Less bandwidth used - only commands need to communicate with the server, no need to send diffs to rerender components.

  • No state sync problems - state is kept only in one place (browser) and the websocket communication used is stateless.

  • No JS or Alpine.js needed except for communication with some third party scripts or widgets, but this can also be solved by creating some standardized libs for popular packages that would handle the interop.

Another important selling point is the dev experience. I envision Hologram to be very friendly to new Elixir converts or beginner devs. I want it to be very, very intuitive, so that you can focus on working on new features in your project instead of solving technical problems and writing boilerplate code.
To achieve that Hologram will provide out of the box such things as UI component library (CSS framework agnostic), authentication, authorization, easy debugging (with time travel), caching, localization and some other features that you typically use in a web app.

I think that using Hologram’s approach, i.e. Elixir-JS transpilation, code on client and action/command architecture it’s possible to create something as productive as Rails, but without its shortcomings related to scalability, efficiency, etc.

josevalim

josevalim

Creator of Elixir

I want to clarify this section a bit. This sentence only applies if you try to use LiveView for client-only interactions, but the docs are very clear that you should not use LiveView for that.

I am aware that you know this, I am commenting to avoid confusion in other threads. :slight_smile:

Assuming you have to hit the server, for example to talk to a database, LiveView response time is actually better than regular HTTP requests because it works over an existing authenticated web socket connection. So the work of parsing headers, authenticate users, etc is not done on every server interaction. Hologram and LiveView should be similar here, as both are WebSockets based.

Similarly, Fly.io gets you closer to your users, which means any server-based application could benefit from Fly.io.

In other words, LiveView is for server based interactions and it excels at that, with or without Fly.io. Indeed, for more complex cases on the client, then you need custom JS (or something that aims to fully cover both server and client, like Hologram).

bartblast

bartblast

Creator of Hologram

@christhekeele I initially tried to use ElixirScript, but I couldn’t make it work. When I went through its GitHub issues I realized the creator stopped maintaining the project and focused all of his efforts on Lumen: Still alive? · Issue #495 · elixirscript/elixirscript · GitHub So I tried Lumen, but too much work was in progress. And now it looks that the project is stalled at the moment: Project Status · Issue #698 · lumen/lumen · GitHub

I also tried another route: Erlang → PureScript → JavaScript through Erlscripten, Erlscripten – Transpiling Erlang to Javascript. Yes, you read it right - News and Announcements - æforum but I wasn’t convinced with it, because one of my goals was easy debugging of the transpiled code, and for that it helps if the transpiled code resembles what was actually written in Elixir.

So I decided to write my own transpiler, because that gives me much more control over the output code and I can fix any errors myself. I do this by first converting the code to AST, then building IR (intermediate representation) containing context and metadata, which makes it easier to reason about the code and finally the IR is encoded into JavaScript. You can take a glance at its structure here: hologram/lib/hologram/compiler at master · segmetric/hologram · GitHub

11
Post #4
bryanjos

bryanjos

@bartblast @christhekeele I stopped working on elixirscript mostly because it seemed like it wasn’t providing the value to the community I hoped it would. And bringing the BEAM to the browser is a big task in itself. But sounds like there is some interest in that sort of thing. I’d be interested in helping with an effort for having elixir compile to js. Nx and Gleam’s JS compiler makes me think going in a similar direction would be a good start since you don’t have to solve the BEAM in the browser so much.

bartblast

bartblast

Creator of Hologram
  1. I’m in the final stages now - wrapping up feature testing and fixing minor issues that popped up during testing. We’re very close to having a playable release! :slight_smile:

  2. About Phoenix integration:

    • Authentication: Nope, Hologram will come with its own auth layer (but it’ll be there out of the box). There’s a possibility we could share sessions between Hologram ↔ Phoenix which would make auth work both ways, but I haven’t dug deep into that yet - just have a general idea how it might work.
    • PubSub: Yes, this will definitely work, but in a later release. We’ll have a nice dedicated DSL in pages and components to talk to Phoenix PubSub.
  3. Here are the downsides I can think of:

    • Browser download size: The framework runtime (with commonly used Erlang/Elixir functions) comes in at 427 kB (minified) / 75 kB (gzipped). I haven’t done any optimization yet though, and there are definitely functions in there we don’t need, so I expect we can cut that in half eventually. Plus each page has its own separate bundle that’s way smaller than the runtime bundle - size depends on what the page uses.
    • Client-side code caution: Since we’re auto-transpiling to JavaScript, you’ll need to be careful not to include any secrets in your client code (passwords, keys, etc). Phoenix LiveView doesn’t have this issue since everything runs server-side.
    • Function limitations:
      • You can’t pass anonymous functions between client and server in command parameters
      • So this won’t work: put_command(component, :my_command, a: 1, b: fn x → 2 * x end)
      • But function captures are fine: put_command(component, :my_command, a: 1, b: &DateTime.utc_now/1)
      • I’m still thinking about whether to allow this at all for security reasons
    • DOCTYPE declaration: All pages/layouts need to use the same DOCTYPE and you can’t use template expressions in the DOCTYPE template fragment. But honestly, in 2024 this isn’t really a problem - just use <!DOCTYPE html> everywhere and you’re good to go.

Where Next?

Popular in News & Updates Top

martosaur
InstructorLite - v1.1.1 and v1.1.2 Pass Gemini token in request header rather than in request parameter (as is shown in API reference :...
New
ConnorRigby
This week we added official Nerves support for the OSD32MP1 line of SOMs. Currently we have tested the osd32mp1-brk breakout board, and ...
New
fhunleth
We recently released Nerves 1.6 and corresponding updates to the Nerves new project generator, nerves_bootstrap and our official systems....
New
New
zachdaniel
Hello everyone! We’ve got two big updates on the Ash community today :tada: Discord Support Forum → Elixir Forum The Ash community is g...
New
sorenone
Oban v2.23.0 has been released. Sharpens resilience under database outages, catches misconfigured unique constraints at compile time, an...
New
fhunleth
We’ve released Nerves v1.3.0 with support for Elixir 1.7 and Distillery 2.0! We encountered a few bumps over the past week, but it’s look...
New
zachdaniel
Join us for our first Ash Office Hours next Thursday at 6PM EST! Myself and anyone from the core team who can make it will be answering q...
New
zachdaniel
We recently launched atomic updates, which look like this: update :update do change atomic_update(:score, expr(score + 1)) end # or w...
New
zachdaniel
What if you had full stack types for SPAs built with tech like React, Inertia, Vue, Svelte… but all of the power Ash Framework and Elixir...
New

Other popular topics Top

sergio
In Ruby, I can go: User.find_by(email: "foobar@email.com").update(email: "hello@email.com") How can I do something similar in Elixir? ...
New
dotdotdotPaul
Okay, I'm having a heck of a time trying to figure out how to best handle the validation of belongs_to associations in Ecto. I'm sure I'...
New
lessless
I believe there are people here who are dealing with CSV files import on the daily basis, and since Excel is a really popular tool there ...
New
Jim
As a follow up to my earlier question: I have the code compiling and running but not getting a successful login from the rest server. ...
New
fireproofsocks
I’m working on defining a simple Ecto schema for a table (in PostGres), but I don’t see where I can define a column as NOT NULL. Conside...
New
chrismccord
As promised, the first release candidate of Phoenix 1.3.0 is out! This release focuses on code generators with improved project structure...
New
myronmarston
The Elixir Typespec docs show the following syntax for keyword lists in typespecs: # ... | [key: type] # keyword lis...
New
chrisalley
ExUnit now has describe blocks which is a welcome addition coming from RSpec. In the docs, it states that nested hierarchies of describe ...
New
belgoros
I’m not a pro in using Regex and can’t figure out why the following behaviour happens, especially if we take into account the difference ...
New
magnetic
Hey :wave:t3: Elixir community, I’ve been learning Elixir, and working on some side projects. My editor of choice is VSCode, and althoug...
New

We're in Beta

About us Mission Statement