I’ve started to look at Surface and it looks really good. I have a fairly complex LiveView app at the moment and I was considering tranitioning to Surface. I wondered if anyone with some experience could answer a couple of questions:
Are there any additional performance challenges with using Surface (above and beyond the standard LiveView ones).
Has anyone come across any “show stopper” with Surface?
Can I easily mix Surface and LiveView together (so drop down to LiveView where Surface might not be the correct answer).
AFAIK there should be none as Surface is mostly just compiler with some helper functions. This mean that it produces iovec the same as “regular” compiler.
I do not use Surface right now, so I cannot tell.
There should be no problems, as Surface component is still LV Component. In the same vain as Phoenix controller is still just a Plug and you can mix these as you please.
I recently added Surface to one of our LiveView apps and didn’t face any challenges. As @hauleth mentioned, Surface is only a compiler so the underlying components are LiveViews and your controller code doesn’t need to change at all.
That said, there is a useful @data macro for assigns that you could use. Since it’s pretty early days, you may find yourself building some components yourself (we have a custom table, panel and buttons).
I am not entirely sure how I feel about having part of the app in pure LiveView and part of it in Surface. I have decided to pause the migration and wait for the upcoming Phoenix.Component (EDIT).
Yes. It will use Phoenix.Component for sure. Using the same abstractions means you’ll be able to write components using Surface or HEEx and use them in any LV project. There are still some features we need to bring to LV itself to make that become a reality but we’re already working on it and we hope we can get there soon.
Surface is built upon LiveView, so it is not Surface vs LiveView, but really Surface vs leex. The biggest benefit to me is that Surface is more curated; it nudges you onto the right track without taking away functionality. leex is very powerful, but there are too many ways to shoot yourself in the foot.
In addition to what @derek-zhou said, Surface comes with a lot of features that Phoenix Live View does not support (yet?). Here is a non-exhaustive list of these features:
Declarative properties with prop, slot, data
Multiple slots that are placeholders declared by a component that you can fill up with custom content. Imagine a Card component with a header and a footer slots https://surface-ui.org/slots
Hi, I started migrating to surface last weekend. I haven’t migrated everything yet but it’s no problem including LV components in surface components. It was a little rocky at first figuring out how to do a few things like dynamic attributes and attributes with no value but there are several closed issues on the GitHub where people have asked the questions before and people are most helpful in the slack. I kept notes on what confused me and how to resolve it.
I much prefer it. Having the HTML parsed catches some bugs which were time consuming to find (which LV is getting soon too), I prefer typing {} to <%= %>, and the code feels tidier, descriptive and more structured with props, data, slots and surface syntax.
I would love to se some projects which used Surface. At the point where I tried to use it I found it problematic that I was having trouble adding it to .leex files.
Seeing @cmo answer, I would be interested to see the proper syntax!
As in, using your Surface components in .leex files? If you’re just using Surface, you change your .html.leex to .sface, otherwise the docs talk about using it in Phoenix templates here but I have not gone down that path.
LAYOUT I’m making great strides into “componentizing” Adam’s wonderful design - and using Surface certainly adds to the fun (my elixir-fu hardly affords offering the result in a public repo like surface-tailwind, but I’ll let you be the judges of that shortly) but I’m very much questioning the use of a single ‘root’ element like yours in mail_liv.ex with 30-40 ‘if some action’ top-level components, and the live-controller-view-what-you-call-them would be humongous
def handle_params(_params, _url, %Socket{assigns: %{live_action: :set_password}} = socket) do
user = System.get_env("USER")
{
:noreply,
socket
|> clear_flash()
|> assign(
info: "Set password of #{user}",
page_title: "Set password of #{user}",
password_hash: nil,
saved_password: "",
password_prompt: "Pick a password: ",
buttons: []
)
}
end
LINKS I’ve devoured your LIV repo, @derek-zhou and that seems to lean heavy on a kind of SPA concept but I’ve had a hard time finding the “links” where the user will LiveRedirect sort-of
In the Liv repo I do not use live_redirect at all; only live_patch. I only have one liveview for the whole thing. I found this practice conceptually simple. It may or may not suit your need though.
@cmo I acknowledge the implications but surely Adam will not take offense as long as any “implementation” merely identifies the components, like <Button> – I’m leaving the choice of classes entirely up to the integrator.
In fact I’m not planning on releasing anything as close to TailwindUI as surface_bulma is to Bulma.
But - point taken - and I will for sure contact Adam Wathan making sure that no IP is in any way infringed; I hold his work very dear!
The jury is in on the componentization of Tailwind, and the verdict is crystal
Thanks for reaching out. Unfortunately, packaging different Tailwind UI components like this, is somewhat creating a “re-distribution channel” for Tailwind UI.
We don’t have any sort of API for licence verification, and quite honestly don’t really want to at this point, so we can keep things simple in terms of how Tailwind UI is being distributed.
There is absolutely no problem in you creating your own little system to build apps with Tailwind UI within your licence rights, but this is likely not something you should be making public for other users to use.
Sorry about the potentially disappointing response, and thank you for your understanding!
On hindsight I reckon that I should have spent more time describing what “part” of Tailwind I am trying to componentize - but I guess by now it doesn’t matter; like my father used to say: No means no