msaraiva

msaraiva

Broadway Core Team

Surface - A component-based library for Phoenix LiveView

Surface is an experimental library built on top of Phoenix LiveView and its new LiveComponent API that aims to provide a more declarative way to express and use components in Phoenix. Some of the main features include:

  • Components as modules - they can be stateless, stateful, data-only or compile-time
  • Declarative properties - explicitly declare the inputs (properties) of each component (like React)
  • An HTML-centric templating language with built-in directives (:for, :if, …) and syntactic sugar for attributes (inspired by Vue.js)
  • Contexts - allows parent components to share data with its children without passing them as properties
  • Compile-time checking of components and their properties
  • Integration with editor/tools for warnings, syntax highlighting, jump-to-definition, auto-completion and more

A work-in-progress live demo with more details is available at surface-demo.msaraiva.io.

Installation instructions and other useful information can be found at github.com/msaraiva/surface.

And finally, a VS Code extension that adds support for syntax highlighting is also available at
marketplace.visualstudio.com.

There’s still a lot of work to do and I hope some of you out there might be willing to help me in this journey. Bear in mind that the LiveView Component API has not reached a stable version yet and it’s currently under development, so although we try to keep track of the latest changes as much as possible, there might be temporary incompatibilities between Surface and Phoenix LiveView until a final version is released.

I’d like to thank @chrismccord and the Phoenix Core Team for buying the idea of reusable components and bringing the necessary basic concepts to the core API.

I also want to thank @josevalim, not only for the many valuable insights during development but also for his inestimable help with the main parser.

Happy coding!

-marlus

564 43623 214

Most Liked

msaraiva

msaraiva

Broadway Core Team

Call me stupid but can someone explain the value in having such an abstraction in practice?

I think there’s no need to call you stupid. It’s a valid question. However, I believe that either you didn’t have the opportunity to read the “Getting started” guide at surface-demo.msaraiva.io or, most likely, I did a terrible job trying to explain some of the benefits there. So, please, let me try again.

In the Grid example it looks like you could have put the <table> inside of a template in the render function

Of course I could, but then I’d have missed the whole point of demonstrating how to use children as data. The Grid example was extracted from the section called “Children as data” of the “Getting started” guide mentioned above. That guide is not meant to be a tutorial on how you should design your application, it’s just a guide with simple examples that demonstrates the main features. The way you use those features is up to you.

and it would work the same

Well, it’s an abstraction on top of EEx, so it expected to work the same. Actually, by the end of the day, anything you can do with Surface can be done with EEx, just like anything you can do with React/JSX can also be done with pure Javascript. It’s hard to know when to stop adding abstractions on top of existing ones. There is always a tradeoff.

and be more similar to how templates work with EEx.

I have no intention whatsoever to keep Surface similar to EEx. On the contrary, I don’t want any dependency between Surface’s and EEx’s syntax. Each one of them tries to solve different problems. The main issue with EEx is that it makes no distinction between plain text and HTML (or any other structured format). Everything is treated as text. When using EEx, all you end up with is a big unstructured list of lists of chunks of text, consequently, a lot of useful information that we could use in our favour to boost productivity is lost. Here are some, IMO, clear benefits of keeping that information:

Normalized syntax for HTML elements and components

Let’s take a look at how HTML elements and Phoenix components are defined in EEx:

  <input style="padding: 1px">
  <%= live_component(@socket, Input, style: "padding: 1px") %>

The syntax is completely different. One is declarative and clean, the other is a function call inside a weird <%= ... %>.

Now let’s take a look at how HTML elements and Surface components are defined:

  <input style="padding: 1px">
  <Input style="padding: 1px">

Now both definitions are declarative, clean and use the same syntax. That makes the reading experience much more pleasant. I can also read it much faster, not only because there’s less noise but also because I don’t have to keep switching contexts (HTML ↔ EEx) all the time. This is only possible because we know that <Input> is a component. We didn’t’ lose that information so we can generate the necessary code to initialize it.

Syntactic sugar for attributes/properties

There’s an example of this feature in the “Getting Started” guide. I’ll just write a shorter version here to save us some time.

Imagine you want to create a button component that sets CSS classes based on the following
rules:

  • button - always set
  • is-loading - set if @loading is truthy

so assuminng @loading is true, the following code should be generated:

<button class="button is-loading">

if it’s false:

<button class="button">

Using Surface we can achieve what we want by just:

<button class={{ "button", isLoading: @loading }}>

Do you see how clean that code looks without any conditional or ugly string concatenation?

Now you can argue that we could achieve the same result with EEx by creating a function. Again, of course you can. After all, that’s exactly how it’s implemented under the hood.

We could also extend this very same concept to boolean attributes like disabled or readonly and create another function so we can handle those too. Something like:

<button class=<%= css_class(["button", isLoading: @loading]) %> <%= boolean_attr(:disabled, @disabled) %>>

Well, that works for sure. But I must confess that it makes my eyes bleed. Could Surface do any better? Let’s see:

<button class={{ "button", isLoading: @loading }} disabled={{ @disabled }}>

Doesn’t it look better? I truly believe it does.

Static checking

Grouping and traversing children

A parent component can classify its children in different logical groups and later traverse them and make decisions based on the information retrieved. They are not just dumb unstructured chunks of text. The concept of parent and child is not lost.

Tooling

  • Syntax highlighting - Since EEx allows you to create incomplete/invalid HTML code, it might get tricky to make syntax highlighting work properly when mixing HTML with EEx/Elixir code. Code written in Surface, on the other hand, is structured, predictable and validated at compile-time. It took me just a couple of hours to create a VS Code extension for it.

  • Auto-complete - Since information about components, properties and data (state assigns) are always available for introspection, it was trivial to add this feature to ElxirSense.

  • Documentation, Go-to-Definition and … a bunch of other related stuff around tooling.

Example of auto-complete/suggestions of assigns:

Example of auto-complete/suggestions of properties and directives:

I could keep going on and on, showing more benefits of the proposed abstraction and its positive impact on productivity and maintainability. I could also try to introduce some of the planned features like scoped styles or slots, but I’m afraid that, if I haven’t convinced you yet, keep trying is not going to make any difference.

BTW, it’s totally fine if you don’t see any value in the solution. As I mentioned before, choosing the write abstraction is hard and will depend heavily on the requirements of the project in hand. I don’t’ have any expectation that Surface or even LiveView will always be a good choice for all kinds of projects. There’s still a long way to go, lots of ideas to be validated and certainly many mistakes to be made. The one thing I believe is that keeping an open mind, trying to explore new ideas to improve existing solutions will always be beneficial to any ecosystem.

Cheers.

Malian

Malian

Hi folks!

Last week, Surface v0.3.0 has been released and it’s now available on hex.

This version improves the Surface Catalogue API which was previously released in v0.2.0 and also introduces a new surface compiler that allows autoloading colocated JS hooks.

Source code and installation instructions for the Surface Catalogue can be found at GitHub - surface-ui/surface_catalogue: An initial prototype of the Surface Catalogue · GitHub. If you want to see it in action, there’s a short video available at Marlus Saraiva on X: "Introducing the new Surface Catalogue. A first attempt to build something similar to Storybook for Surface and, hopefully, for any Phoenix/Liveview project in the near future. Prototype and installation instructions at https://t.co/rzden5u98g @phoenixframework #myelixirstatus https://t.co/xO2QHbmdqX" / X. The full changelog can also be found at surface/CHANGELOG.md at main · surface-ui/surface · GitHub.

We are happy to see that Surface is becoming more and more popular. There’s big a chance Surface is already part of many developers stacks, so it becomes essential to support these developers to have a stable version 1.0 of Surface.

Let’s not be afraid of words, our ambition is to make Surface the best possible platform for creating LV projects! To move forward on this path, we’re pleased to announce that @Malian and @miguel-s have joined our core team and will be helping us to improve Surface.

If you would like to shape the future with us, report issues or simply need help, feel free to join us on the slack channel or on github!

Happy coding!

msaraiva

msaraiva

Broadway Core Team

Hi folks!

Surface v0.1.0-alpha.2 has just been released with lots of bug fixes and new components. Here are the highlights:

Along with the new built-in components, there are also some new UI components available. For documentation and live examples see:

Source code can found at GitHub - surface-ui/surface_bulma: A set of simple Surface components based on Bulma · GitHub.

The Markdown component

The new <#Markdown> component allows users to write markdown content directly in a Surface template. The content will be validated and translated to HTML at compile-time.

Surface’s website was mostly written using this component. It’s very handy, especially if you want to mix static markdown and HTML content with runtime interactive components.

Example:

Reporting syntax errors:

If you’re using VS Code, there’s a new version of the Surface extension which adds syntax highlighting to the Markdown component’s content, as you can see in the examples above.

Have fun.

Where Next?

Popular in Announcing Top

OvermindDL1
I created a new library (rather I pulled out a couple files from my big project), it manages an operating system PID file for the BEAM. ...
New
ityonemo
Currently just starting out on a new mini-project - getting zig NIFs to run in elixir. https://github.com/ityonemo/zigler The idea here...
New
ostinelli
Let’s write a database! Well not really, but I think it’s a little sad that there doesn’t seem to be a simple in-memory distributed KV da...
New
dominicletz
Hi, I thought I had posted my library before but seems I hadn’t. The project is still in early stages but it’s growing and so I think it...
New
woutdp
Hi! I wanted to introduce my latest project LiveSvelte. It allows you to render Svelte inside LiveView with end-to-end reactivity. It’s ...
New
oltarasenko
Dear Elixir community, After a year of development, bug fixes, and improvements, we are proudly ready to share the release of Crawly 0.1...
New
aditya7iyengar
Rummage.Ecto and Rummage.Phoenix provide ways to perform Searching, Sorting and Pagination over Ecto queries and Phoenix collections. Fo...
New
Azolo
Hey everyone, I just released WebSockex which is a Elixir WebSocket client. WebSockex strives to work as a OTP special process, be RFC6...
New
josevalim
Hello everyone, We have just released NimbleCSV which is a small and fast CSV parsing library for Elixir. It allows developers to define...
New
wfgilman
I’ve cleaned up and open sourced three financial libraries I was using for my company. They are bindings for the APIs of these three comp...
New

Other popular topics Top

siddhant3030
Hi, I have to write a raw query for one of my project. But till now I have used ecto queries and don’t have much experience writing raw ...
New
electic
Hi, I am new to Elixir. I am trying to use the DateTime component to insert a date into MySQL however the there seems to be no way to fo...
New
jay1
Why is it that the mnesia database isn’t the most preferred database for use in Elixir/Phoenix?
New
vegabook
I’m brand new to Phoenix and I have stripped one of the demo applications to the bone. I just want to get an svg up on the screen. Here i...
New
joeerl
Hello again - after a longish gap I’ve decided I really must dig into Elixir and see what’s been happening here - so I have a few questio...
New
Emily
I have VueJS GUIs with the project generated using Webpack. I have Elixir modules that will need to be used by the VueJS GUIs. I forese...
New
bsollish-terakeet
Credo is smart enough to check for (something like) this: assert length(the_list) == 0 with this response: Checking if an enum is empt...
New
dblack
I’ve got an issue with an app and I’ve no idea of how to troubleshoot it. I’m hoping someone here might have seen something similar. I p...
New
sergio
Kind of like when jquery came out, it was super necessary. Existing drag and drop libraries have a bunch of baggage to support old browse...
New
vonH
In asking this question I am more interested about the expressiveness of the language itself and less concerned about the availability of...
New

We're in Beta

About us Mission Statement