New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Extending class
Attribute Support
#1492
Comments
I want to say, first of all: DX-wise, this would be a clear win. If the only goal were to make the framework as flexible as possible and easy for developers to use as possible, I'd add this in a heartbeat. I also want to explain why it isn't already available, and suggest some possible work-arounds. Background: Server-Rendering OptimizationsImagine a component like this: Your Code #[component]
fn ExampleComponent(label: String, first_item: String) -> impl IntoView {
view! {
<div class="some-class">
<strong>{label}</strong>
<ul>
<li>{first_item}</li>
<li>"B"</li>
<li>"C"</li>
</ul>
</div>
<span>"Hi!"</span>
}
} Macro Output (CSR/Hydrate) use ::leptos::leptos_dom::html::*;
fn ExampleComponent(label: String, first_item: String) -> impl IntoView {
Fragment::new([
div()
.attr("class", "some-class")
.child(strong().child(label))
.child(
ul().child(li().child(first_item))
.child(li().child("B"))
.child(li().child("C")),
),
span().child("Hi!"),
])
} That looks perfectly reasonable. It's not the fastest thing in the browser (we have a On the server, though, it can be improved by a lot. Almost all of the What if we could just do... Optimized SSR Output fn ExampleComponent(label: String, first_item: String) -> impl IntoView {
HtmlElement::from_html(format!(
r#"<div class="some-class">
<strong>{}</strong>
<ul>
<li>{}</li>
<li>B</li>
<li>C</li>
</ul>
</div>
<span>Hi</span>"#,
label,
first_item
))
} Wouldn't that be faster? Yes. And it is. In fact it's much, much faster. Obviously depending on the circumstances how much faster varies, but for a typical page the benchmarks I've done suggest it's something like a 60-70% improvement, i.e., it makes your server rendering 2-3x faster. And so it's exactly what we do. Rather than generating a tree of a bunch of virtualized DOM nodes/HTML elements, Leptos rendering on the server generates, at compile time, a template HTML string, literally using The ProblemI hope that wasn't too long a digression, and I hope it makes the problem more apparent. For the purposes of optimization the However it means that it becomes harder to do nice things like this request. If I want to use
during server rendering, how should it work? Do we need to start parsing HTML to find each top-level element and apply the class? Should we parse that element to find out if it has a class attribute? Should we parse that class attribute to see if it already has I'm not saying this sarcastically or as if it's a bad idea to do that. Maybe it's actually a good idea, I don't know! But it's definitely harder than it would be without these optimizations. There's already a parallel case
This really does iterate over the top-level HTML elements of The reason we can do this with Possible Paths Forward
So yeah I'd say Option 3 is the best bet for now but maybe this would be possible in the future, at some runtime cost. |
First of all, thank you for taking your valuable time and giving me such an extensive answer. I hope, I have been able to follow along enough to understand the problem. I fully agree that taking 30% of the possible performance, just to improve DX a little bit, is no acceptable solution. I have some ideas for Option 1 with probably less performance impact, but I don't know if any of them are acceptable in terms of complexity and maintainability. Possible Solutions for Option 1A) My first attempt would have been to additionally interpolate the classes. The For a single top-level element, that might look something like this.
That might work, but I don't know how much it would hurt rendering performance. It might still be acceptable. However, if this would be implemented, a few months from now, someone might come along and kindly ask the same for the B) At some point, then, it might just as well be a solution to return the top-level element(s) as virtualized DOM nodes, with just their children being rendered as template string (passed along as C) We could still do a hybrid-hybrid approach. A component could offer two functions for rendering when using SSR: one function (F1) generating the complete template string as usual for situations, where this is sufficient. The second function (F2) would return the result of idea A or B. We would "just" need some way to distinguish within the parent's We would need to know, whether some of the attributes given to the component instance are HTML-native and would thus require calling F2. There is (I guess) no way to know inside a proc-macro, which type of DOM node (with it's native attributes) the component returns. We could establish some convention, that such HTML-native attributes on components would need to be prefixed somehow (something like D) The last idea would be to allow native attributes on components only for client-side rendering. That, however, might be to inconsistent for the user, I don't know. These are my ideas. 😅 I hope, my explanations were (kind of) understandable. If none of these options seem acceptable for you and this thread is a dead-end, feel free to close the issue. 🙂 |
Is your feature request related to a problem? Please describe.
When using my own custom components, I sometimes have to attach an additional CSS class to them, that applies only to individual instances. Modifying the
width
would be an example application.Describe the solution you'd like
class
attribute support on custom components, so that classes could be attached to custom components like they were native HTML tagsOption
would be nice, too. The exact type definition would need some discussion, though, since you cannot doOption<impl Into<Cow<...>>>
without trait objects (?)Describe alternatives you've considered
For now, I integrated an additional
class
prop that takes anOption<&'static str>
into my custom components. I implemented a trait method onHtmlElement
taking that optional class and attaching it to the element, since I found no way to do that directly in theview!
macro. But integrating this for every component is some overhead. This also does not support the syntactic sugar theview!
macro usually provides for working with classes.Another alternative would be to add an explicit
width
prop (to stay with the example from above), but that would be quite limiting. However, maybe it is intended like that?The text was updated successfully, but these errors were encountered: