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 42634 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

  • Syntax checking - Since EEx doesn’t care about the structure of your code, any invalid HTML will only raise errors at runtime. When using Surface, most checks are done at compile-time.

  • Validation of properties and children - you can restrict what kind of properties and children a component accepts.

  • Other examples of static checking are available at GitHub - surface-ui/surface: A server-side rendering component library for Phoenix

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. If you want to see it in action, there’s a short video available at https://twitter.com/MarlusSaraiva/status/1360254701808324615. The full changelog can also be found at surface/CHANGELOG.md at master · msaraiva/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.

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.

msaraiva

msaraiva

Broadway Core Team

Hi folks!

Surface v0.1.0 has been released and it’s available on hex.

Many thanks to all contributors who made this release possible. There’s no way we’d get this far without your help.

Also many thanks to all Phoenix Core Team for creating LiveView and its amazing component API, which is the foundation that made this project come true.

Happy coding!
-marlus

msaraiva

msaraiva

Broadway Core Team

Where Next?

Popular in Libraries Top

OvermindDL1
Been making an MLElixir thing (not released yet…) for fun in spare time in the past day. I’m just trying to see how much I can get an ML...
132 13487 106
New
tmbb
I’ve decided to create this topic to discuss optimization possibilities for something like Phoenix LiveView. I’ve created this topic unde...
New
RobertDober
Earmark is a pure-Elixir Markdown converter. It is intended to be used as a library (just call Earmark.as_html), but can also be used as...
239 11851 134
New
kip
Image is an image processing library for Elixir. It is based upon the fabulous vix library that provides a libvips wrapper for Elixir. I...
574 16576 179
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
asiniy
Hey there! I wrote a download elixir package which does exactly what its name about - an easy way to download files. I saw solutions ...
New
archan937
It is a well-know topic within the Elixir community: “To mock or not to mock? :)” Every alchemist probably has his / her own opinion con...
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
MRdotB
I needed to reuse React components from my Chrome extension in my Phoenix/LiveView backend. I noticed that for Svelte/Vue, there are live...
New
msaraiva
Surface is an experimental library built on top of Phoenix LiveView and its new LiveComponent API that aims to provide a more declarative...
564 42633 214
New

Other popular topics Top

lanycrost
Hi everyone! I need implement if…else if…else condition from my elixir code, and anymore of this control flow structures not work proper...
New
TunkShif
This post is an instruction guide to help you setup your Neovim for Elixir development from scratch. It includes general information on h...
273 38985 115
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
vonH
In asking this question I am more interested about the expressiveness of the language itself and less concerned about the availability of...
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
JorisKok
I have a server on AWS, and was running a load test using artillery. When looking at the Phoenix dashboard I see the Ports going to 100% ...
New
aalberti333
As the title describes, I’m trying to run Enum.map() over a list of key/value pairs, where the value is a map. My data looks like this: ...
New
sergio_101
I am VERY much an elixir newbie. I have taken one elixir course and one phoenix course on Udemy. During that course, I saw the instructor...
New
_russellb
I want to try my hand at web scraping. What tools/libraries do I need to use. I’m hoping to turn this into something professional so don’...
New
qwerescape
Is there a way to get the call stack or stack trace at any point in the code? Not from exceptions, but an expression that returns how the...
New

Sub Categories:

We're in Beta

About us Mission Statement