Replies: 2 comments
-
Nothing client-side can compare to server-rendered content. That's why we're working on improving the SSR story. Alas, we're on the bleeding edge of what the platform can do, so this takes time. Firefox, for example, didn't ship Declarative Shadow DOM until earlier this year.
FWIW this story isn't great either because it's seldom executed well. I can't count the times, especially on mobile, where I get to a page and try to click something a few times but it does absolutely nothing. And then there's zero indication when the button actually starts to work so users are left guessing and feeling like things are broken. I'd rather see a spinner than have to guess when I can use the app. Aside: that technique is a facade that emerged to offset poor application architecture where large amounts of client-side code caused poor Lighthouse metrics. It can work when the amount of code needed for interactivity is reasonable and split properly, but that requires discipline and, in practice, developers — especially those in large orgs — manage to craft poor experiences even with SSR. You have to load all that code within a few hundred milliseconds, otherwise users will notice. That's just impossible on a lot of mobile networks, but I digress…
This seems incredibly hard to maintain, especially for a library such as Shoelace that's used in a multitude of websites and applications. Even a single pixel shift will quickly feel janky, and I don't see how we'd manage to make light DOM elements look exactly like shadow DOM elements because of the latter's encapsulation. A simple CSS reset will cause a world of trouble. Do we add a complex build step to copy styles into the light DOM with This doesn't seem like a promising thing to build or maintain :(
DSD is the platform's intended solution for this and, although experimental, Lit's SSR module is the most advanced I've seen that we can consider for Shoelace. The platform takes time, but modules like this have to come after so they take even more time. I can appreciate that bleeding edge isn't for everyone, but I can't envision a simpler way to do "light-to-shadow DOM swapping." It's still going to require a build step if you want the DX to be the same for consumers, otherwise you're still waiting for all that code to load/run. Without a build step, I can only envision it looking something like Bootstrap — and there's a lot of room for error if users have to scaffold out something like this for every component. <sl-button variant="primary" type="submit" loading>
<button class="primary" type="submit">
Submit
<sl-spinner role="role="progressbar" aria-label="Loading"></sl-spinner>
</button>
</sl-button> If I understand correctly, will that get replaced with the current syntax at runtime? <sl-button variant="primary" type="submit" loading">Submit</sl-button> Maybe I'm misunderstanding the approach, but I can't figure out how it would be simpler than plugging an SSR module into 11ty, Next.js, or whatever metaframework once we finish adding support for it. Shoelace isn't 100% ready for this yet, but once we are it will look something like this: // .eleventy.js
import { components } from '@shoelace-style/shoelace/dist/ssr.js';
import litPlugin from '@lit-labs/eleventy-plugin-lit';
export function (eleventyConfig) {
eleventyConfig.addPlugin(litPlugin, {
mode: 'worker',
componentModules: components
});
}; Our goal is to make SSR this easy for as many metaframeworks as we can. I think we should continue spending effort on this instead of inventing our own solution. |
Beta Was this translation helpful? Give feedback.
-
@claviska Thank you, this was a great response and I agree with pretty much everything you say. The approach I've had to take is definitely hard to maintain, which is why I was interested in starting a discussion around possible options. Unfortunately many of my projects are in the context of Shopify themes, where we can't use SSR unless we go fully headless which presents it's own set of disadvantages. I have composed Liquid/HTML, CSS, and a script tag with a Lit component definition into a single file, and share common styles between light and shadow DOM using a Liquid I was thinking that having a framework for showing at least something in the light DOM before the shadow DOM is attached could be a powerful method to achieve something similar to the benefits of SSR, but I guess neither of us know how this could be turned into a pattern that is maintainable enough for most people to want to use. What you say about spinners makes sense, except we have to consider that even with something like a carousel that contains the LCP image above the fold, good TTI scores won't compensate for bad LCP scores, and result in this component not being useable for many people because we care more about the largest image paint being visible than the carouse controls being clickable. However, given this general preference for seeing loaders rather than non-interactive content or layout shift, maybe there is a conversation around skeleton loading? I've been using light DOM for this too, which works nicely with dynamic updates (especially using Lit Async Tasks) because even after the shadow DOM has been attached, you can conditionally show the rendered content or fallback on the light DOM content via the default slot. The Shoelace skeleton loader renders inside the shadow DOM meaning we weren't able to use it for performance reasons. This also applies to other things like Maybe your answer is simply that Shoelace/Web Awesome as a library cares more about TTI than about LCP, and if you need to improve the latter, it will only be possible in the future using SSR and a platform that supports it. I think this is a reasonable response, but also think it's an important discussion to be had. Perhaps the best solution is a section in the library's docs discussing this and explaining the TTI benefits of Shoelace/Lit/native custom elements? |
Beta Was this translation helpful? Give feedback.
-
I have been using native web components, Lit, and Shoelace on some major projects in production.
Mileage has been excellent in general, with a couple of exceptions, one of which I'll detail here: Shoelace has deal-breaking performance issues, especially for above-fold content when the Largest Contentful Paint (LCP) is inside a Shoelace component.
This is because nothing is displayed until the JS assets have finished fetching, the code executes, and the shadow DOM gets attached. This vital performance metric cannot compare therefore with something like server-side rendered React with hydration, where markup is immediately delivered with the initial DOM, albeit not yet interactive until hydrated. Shoelace/web components may technically have better Time To Interactive (TTI) scores, but as a user, LCP has a noticeable impact on perceived performance. It also means that nothing at all will be displayed if JS is disabled in the browser.
In my custom build native and Lit web components, I am leveraging a technique we could coin "Light-to-Shadow DOM Swapping" First, we render the best approximation of the content inside the light DOM as a fallback, which gets immediately displayed as part of the initial DOM render. This is then replaced (or progressively enhanced) with the shadow DOM version of the content once the JS executes and it gets attached. If the light DOM version is accurate enough, there should be minimal or no perceived visual change when the content becomes interactive. This also means that useful markup for SEO or JS-disabled environments can be rendered straight away.
To achieve this using Shoelace, I am wrapping the component inside my own custom element, which tries to imitate the Shoelace component's layout with initial markup/styling in the light DOM, to reduce perceivable difference once it's replaced with the Shoelace version (for instance,
sl-carousel
). Without doing this, I wouldn't be able to use something like a Shoelace carousel for above-fold content due to the LCP requirements my clients normally have. This does come at the cost of maintainability, as we need to keep this light DOM version in sync with any changes to the shadow DOM version.I would like to begin a discussion about addressing these issues with performance and SEO. I know one approach would be to leverage SRR with Lit, but the Lit SSR package is still experimental, and this would require templates to be built server-side thus increasing complexity or being simply unfeasible for many cases. With this in mind, I am interested in thoughts around leveraging this Light-to-Shadow DOM Swapping technique or something similar, as it retains the ethos of staying close to native APIs without extra tooling. If we can bake this level of performance into Shoelace, or at least document a framework for doing so, I think it would be incredibly beneficial and make Shoelace/Web Awesome a much stronger sell across projects.
Tagging @claviska and hoping you're interested in discussing
Beta Was this translation helpful? Give feedback.
All reactions