Is the `solid-element` library still being maintained?

I'm working with an engineering team at SuperTokens to create a set of reusable Web Components, and the team was interested in using solid-element to get the job done. However, there doesn't seem to be extensive documentation for this tool, and it hasn't gotten an update for quite some time. Is the recommendation to use something else like lit-html for creating Web Components? And is it safe to assume that solid-element is "Use at your own risk", or will it be getting more attention later?
22 Replies
peerreynders
peerreynders•7mo ago
https://dev.to/ryansolid/maybe-web-components-are-not-the-future-hfh not all "components" need to be DOM elements https://keithjgrant.com/posts/2023/07/web-components-arent-components/ I leave you to draw your own conclusions. Also if you want to support WC-SSR in any form Lit is probably your safest bet. Of course there is the Preact signals integration. I also think it's a matter of priorities. Over the past year it's been SolidStart, now Solid 2.0. It's not inconceivable that an update may happen post-2.0.
DEV Community
Maybe Web Components are not the Future?
I know you are thinking, yet another article in this back and forth between Web Component proponents...
Ryan Carniato
YouTube
Let's look at Web Components
In the spirit of looking at technology I haven't covered on stream, and that I have a long history with, this week we will look at Web Components. There has been a struggle between frameworks and "the platform" that has culminated around them that is worth exploring, plus looking at tools and approaches to using them. [0:00] Introduction [5:42]...
Web Components Aren’t Components
Modern web frameworks are built entirely on the concept of components. In the realm of these frameworks, a component is a reusable piece of application code. Al
lit.dev
Server-side rendering (SSR) – Lit
Simple. Fast. Web Components.
lit.dev
Lit 3.0 Prerelease 2 and more!
Today we are publishing the second prerelease of Lit 3.0.
bigmistqke
bigmistqke•7mo ago
I don't know too much about web components myself, but maybe @trusktr (Joe Pea) can help since he is a big proponent of solid and web components.
XxX_MLG Noob_XxX
XxX_MLG Noob_XxXOP•7mo ago
Thanks for linking the articles. I think the difficult thing for us is that, ideally, we'd have a library of reusable components that work in any environment and that are more future proof. To my knowledge, the components we need wouldn't be doing anything too crazy either. That seemed to be the use case that Ryan said WCs can be useful for, which I agree with. If we were building a full-on web app, we probably wouldn't be considering WCs. But because of the use case I mentioned, that's our focus. SSR WCs would also be desirable as long as it wouldn't require too much effort in userland.
peerreynders
peerreynders•7mo ago
Right now it looks like solid-element has some friction with TypeScript. https://discord.com/channels/722131463138705510/1251556278963146845/1251556278963146845
SSR WCs would also be desirable
Well, there is no-such-thing because that was never part of their client-centric design which is why as a feature SSR firmly locks you into a WC-framework which seems like a philosophical contradiction. https://lume.io/ is a showcase of the CE/WC API and Solid being used in concert but on a lower level than solid-element.
Joe Pea
Joe Pea•7mo ago
I wrote Lume Element, which is an alternative to solid-element, and still maintained: https://GitHub.com/lume/element
GitHub
GitHub - lume/element: Fast and simple custom elements.
Fast and simple custom elements. Contribute to lume/element development by creating an account on GitHub.
Joe Pea
Joe Pea•7mo ago
I wouldn't say it is lower level than solid-element, but the dev experience is built around the actual custom element classes instead of function wrappers. I have a WIP example for SSR and SSG coming out soon for Astro. I'd love to get it working in Solid Start too, but that might require some updates to Solid Start. Just to avoid confusion, the 3D elements from the lume library are built on top of the @lume/element library. The @lume/element library can be used as alternative to solid-element, Lit, and other Custom Element libs and does not have anything 3D-specific in it. Don't listen to the above article, Custom Elements are the future. @peerreynders posted only negative bias articles. Custom Elements, especially without any build tools, are very liberating. Lit elements, for example, can be written without any build tools, and an optional build can be added to compile the templates to something more optimized for startup time. Lume Element doesn't have an html template tag compiler yet, but if you want to use a build you can use Solid JSX instead of html inside your templates to optimize. But keep in mind that for most apps, this optimization is premature. Also! Custom Elements have a great dev experience: you can inspect them in the devtools element inspector of any browser, out of the box! With non-custom-element component systems, you have to install browser-specific devtools, if they even exist. The truth is that the form of reactivity in Solid is the best type of reactivity we have in JS (signals and effects, after which the new TC39 Signals spec for JavaScript is named), and hence the best for custom elements. (Lit's reactivity is diff-based like React, more overhead, but honestly the difference doesn't matter for most apps anyway). Lume is also TypeScript-friendly. The custom elements in JSX can be type checked.
SSR WCs would also be desirable as long as it wouldn't require too much effort in userland.
It may require following some conventions, for example put client-side logic only in connectedCallback, as that will run only on the client, or similar. I'm working out these details for Lume, but Lit has similar rules for SSR. If you really need SSR now, I'd say search for Lit Astro. If you don't need SSR now, Lume Element dev paradigm is a lot better with signals and effects imo.
solid-element is "Use at your own risk", or will it be getting more attention later? (edited)
There's been chat about updating solid-element ( @davedbase 🌈 ), and I've proposed an update to get class-based style added to it based on my years-worth of Lume dev (with many unit tests). I've built with Lume Element at Velodyne Lidar, NASA, SpaceX, and now at Uthana (AI-generated 3D animation). Rest assured if you need help with Lume Element, I'm not going anywhere just ping me (or join Lume Discord).
davedbase
davedbase•7mo ago
The class based approach doesn’t fit with Solid’s philosophy unfortunately. It’s best that Lume Element exists in its own form so that it’s not tainted by Solid’s ideology or opinions.
Joe Pea
Joe Pea•7mo ago
Hah. Lume is tainted by Solid (not a bad thing), for it uses Solid signals and templating.
davedbase
davedbase•7mo ago
I mean it’s a bit of both. lol they are just unique in their ideals
Joe Pea
Joe Pea•7mo ago
How so?
davedbase
davedbase•7mo ago
It comes down to opinions and ideology ultimately. It’s preferences. Decorators and classes are not common in Solid ecosystem and probably won’t be as far as I can tell. Btw just stating what I’ve come to understand. I’m not looking for a debate of if that’s good or bad. I think if users prefer Lume Element and that style it’s a fantastic tool As for Solid Element, it probably needs work to define how it can be improved in a Solidlike way. Lume Element has had years of work though and is pretty awesome in its own right
XxX_MLG Noob_XxX
XxX_MLG Noob_XxXOP•7mo ago
Well, there is no-such-thing
I'm aware, though I can't tell if the declarative shadow DOM is supposed to help with that. I probably could've chosen my words better. The plan is to use some kind of tool to help with Web Components regardless, since it'll make the development faster and easier. It's just a question of what tool will help us accomplish our goals best. Those are a lot of helpful insights and resources! Thanks so much! I'll give Lume a look. I do like the idea of being able to skip a build step with Lit. But I think the dev team likes the idea of having something Solid-esque more. So Lume sounds potentially promising. If we do end up working with that tool, I'll probably end up having to ping you. 😅 So thanks for being willing to be spammed. (I won't actually spam you. lol. That would be bad. But I'll still ask questions. Haha.)
davedbase
davedbase•7mo ago
Joe’s great! Always willing to help out. I think you can contract him too 😉 haha He has a great vision for how Solid reactivity can help the WC community as well.
Joe Pea
Joe Pea•7mo ago
Hah, I'll try my best to answer all Q's, if you decide to try it out. @XxX_MLG Noob_XxX My recommendation would be to take both solid-element and Lume Element for a test run (and even Lit), so you can get a feel for what they offer and how they differ. solid-element gives a function-based wrapper around custom elements, but because of the way it is implemented, it makes it very inflexible compared to raw classes. For example, solid-element offers no control over the attribute types. All attribute string values always get passed in as-is. But Lit and Lume Element both offer control over this. For example @numberAttribute foo = 123 in Lume (and something similar on Lit) will ensure that an attribute with the value "123" gets coerced into a number. With solid-element, you will need to implement all the string coercions yourself.
@element('my-el')
class MyEl extends Element {
@numberAttribute foo = 123
}
@element('my-el')
class MyEl extends Element {
@numberAttribute foo = 123
}
<!-- in HTML: -->
<my-el foo="456"></my-el>
<!-- in HTML: -->
<my-el foo="456"></my-el>
All of three offer a way to specify to us ShadowRoot or not. But solid-element does not provide scoped style when opted out of the ShadowRoot. With solid-element, if you want to for example add a method to your element, you'll need to add it onto the element reference, which will not be supported for type checking in TypeScript:
customElement('my-component', {someProp: 'one', otherProp: 'two'}, (props, { element }) => {
element.someMethod = () => { // type error in TypeScript
doSomethingWithElement(element, parseFloat(props.foo ?? element.getAttribute('foo')))
}
})
customElement('my-component', {someProp: 'one', otherProp: 'two'}, (props, { element }) => {
element.someMethod = () => { // type error in TypeScript
doSomethingWithElement(element, parseFloat(props.foo ?? element.getAttribute('foo')))
}
})
With Lume or Lit, you'd just define the method in your class, and it will be natively supported by TypeScript:
@element('my-el')
class MyEl extends Element {
@numberAttribute foo = 123 // and similar with Lit
someMethod() {
doSomethingWithElement(this, this.foo)
}
}
@element('my-el')
class MyEl extends Element {
@numberAttribute foo = 123 // and similar with Lit
someMethod() {
doSomethingWithElement(this, this.foo)
}
}
With Lume and Lit, you implementation is more flexible because you can use more patterns like mixins. For example, let's use a mixin that added childConnectedCallback() to our class for easily handling connected children:
@element('my-el')
class MyEl extends WithChildren(Element) {
/*..same as before..*/

childConnectedCallback(child) {
console.log('child connected', child)
this.someMethod()
}
}
@element('my-el')
class MyEl extends WithChildren(Element) {
/*..same as before..*/

childConnectedCallback(child) {
console.log('child connected', child)
this.someMethod()
}
}
(Check out the wrapper that creates the custom element class for solid-element here: https://github.com/ryansolid/component-register/blob/master/src/element.ts) Hiding the element class definition just to make it a function is, in my opinion, unnecessarily limiting, fights against the grain of web standards, and introduces new issues to solve such as out-of-the-box TypeScript support eliminated (f.e. element methods). Now what if you want to use ElementInternals? Well guess what, you need to access this in your Custom Element class, which is hidden in solid-element! Every new feature that is added to Custom Elements, solid-element will need to expose it, which is unnecessary work. With Lume or Lit, you can just use this.attachInternals() in your class constructor as you see fit, without Lume or Lit having to adjust to expose it to you. With solid-element, you cannot make base element classes to extend from, you're limited only to composition. Sometimes composition is not desirable, and sometimes inheritance is. @davedbase 🌈 ^ differences between solid-element and class-based libs like Lume and Lit you might like to be aware of. At a high level, it is also worth noting that if you've written this,
function SomeComponent() { // and similar with solid-element
const [foo, setFoo] = createSignal(0)

createEffect(() => {foo()})
}
function SomeComponent() { // and similar with solid-element
const [foo, setFoo] = createSignal(0)

createEffect(() => {foo()})
}
you've essentially written a class with all properties being private. That's how classes were first implemented before class syntax existed to make it easier, syntactically cleaner, and more flexible.
XxX_MLG Noob_XxX
XxX_MLG Noob_XxXOP•7mo ago
Those are some helpful comparisons. Yeah I've only played a tiny bit with solid-element and I quickly noticed the limitations of having to attaching things to the element instance (instead of being able to define them on the class directly). Does Lume's JSX support/have something like Solid's <Show>/<For>?
Joe Pea
Joe Pea•7mo ago
Yeah, it is Solid's JSX. But you can also use Solid's html for buildless. It is flexible, all you need to do is return DOM, or Solid template result (JSX/html), from template, just as with a function component. Basically:
@element('my-el')
class MyEl extends Element {
template = () => <div>
{/*this is just Solid JSX*/}
</div>
}
@element('my-el')
class MyEl extends Element {
template = () => <div>
{/*this is just Solid JSX*/}
</div>
}
Behind the scenes, the Element.connectedCallback() method simply calls Solid's render, something like render(this.template, this), so essentially the template method is like a Solid component. You can even write template in a similar way to a solid component, for example:
@element('my-el')
class MyEl extends Element {
@numberAttribute foo = 123

template() {
const [bar, setBar] = createSignal(456)
return <div>
{this.foo}
{bar()}
</div>
}
}
@element('my-el')
class MyEl extends Element {
@numberAttribute foo = 123

template() {
const [bar, setBar] = createSignal(456)
return <div>
{this.foo}
{bar()}
</div>
}
}
This is nifty, because can use Solid "hooks" inside of the template method. F.e. anything from solid-primitives, etc. Solid reactivity is so flexible, you could work with DOM in any way you want, for example the following make an element wrapper around some hypothetical jQuery component:
@element('my-el')
class MyEl extends Element {
@numberAttribute foo = 123

template() {
const div = jQuery('<div>').somePlugin(/*whatever*/)

createEffect(() => {
div[0].textContent = this.foo // update textContent when foo changes
})

// simply return the DOM element
return div
}
}
@element('my-el')
class MyEl extends Element {
@numberAttribute foo = 123

template() {
const div = jQuery('<div>').somePlugin(/*whatever*/)

createEffect(() => {
div[0].textContent = this.foo // update textContent when foo changes
})

// simply return the DOM element
return div
}
}
This is so flexible and simple compared to Lit or React for example.
XxX_MLG Noob_XxX
XxX_MLG Noob_XxXOP•7mo ago
Oh wow! Nice! It seems like a component could just be passed to template then. 🤔 Interesting. Does it still work with stuff like Solid's Context too?
davedbase
davedbase•7mo ago
I am curious about this as well. State sharing across components is a concern of mine.
baxuz
baxuz•6mo ago
This works surprisingly well!
/**
* A declarative shadow root component
*
* Hooks into SolidJS' Portal's `useShadow` prop
* to handle shadow DOM and the component lifecycle
*/
const ShadowRoot: ParentComponent = (props) => {
let div: HTMLDivElement;
return (
<div ref={div!}>
<Portal mount={div!} useShadow={true}>
{props.children}
</Portal>
</div>
);
};
/**
* A declarative shadow root component
*
* Hooks into SolidJS' Portal's `useShadow` prop
* to handle shadow DOM and the component lifecycle
*/
const ShadowRoot: ParentComponent = (props) => {
let div: HTMLDivElement;
return (
<div ref={div!}>
<Portal mount={div!} useShadow={true}>
{props.children}
</Portal>
</div>
);
};
Joe Pea
Joe Pea•6mo ago
Yep, you can use any Solid components in the template. Each element is its own reactive root, so currently Solid-style contexts are limited to the shadow root of the element they are in. But we could fix that, I just haven't needed it. I use DOM-based context patterns, instead of the solid-specific pattern, if I need a context. For example a child element deep in the DOM tree can dispatch a bubbling composed event to receive a context, and an element higher up can pass the context to the element (event.target). This is all using plain DOM APIs. To support contexts for Solid, instead of creating a new root for each element, we could perhaps use getOwner and connect to a parent element's reactive context. Child element deep in the DOM, nested in any shadow root down below:
this.dispatchEvent(new Event('foo-context', {bubbles:true, composed: true}))
this.dispatchEvent(new Event('foo-context', {bubbles:true, composed: true}))
Ancestor element up high that provides a context:
this.addEventListener('foo-context', () => {
event.target.setFoo(foo)
})
this.addEventListener('foo-context', () => {
event.target.setFoo(foo)
})
There's a multitude of ways to implement. event.detail could be a callback, etc.
Joe Pea
Joe Pea•6mo ago
GitHub
community-protocols/proposals/context.md at main · webcomponents-cg...
Cross-component coordination protocols. Contribute to webcomponents-cg/community-protocols development by creating an account on GitHub.
Joe Pea
Joe Pea•6mo ago
Lit implements additional API around it using decorators: https://lit.dev/docs/data/context/
lit.dev
Context – Lit
Simple. Fast. Web Components.
Want results from more Discord servers?
Add your server