Hacker News new | past | comments | ask | show | jobs | submit login
Show HN: Cami.js – A no build, web component based reactive framework (github.com/kennyfrc)
113 points by kennyfrc 10 months ago | hide | past | favorite | 34 comments
Hi Everyone! My main motivation for making this was that I wanted something as powerful as Svelte or React but I wanted no build steps, no JSON API, and I wanted something as close to vanilla js as much as possible. I'm mainly a backend developer, and I wanted to simply return html with some interactive islands when I need to (whose components get 'hydrated' with by backend language's templates).

Some key ideas:

• It's centered around light dom web components • Uses a "reactive element", which uses observables for fine-grained reactivity • Rendering is done through lit-html's tagged templates • A pub/sub store • Easy immutability using immer (it powers the observable updates & also the reducers)

It's my first 'serious' library that I'm using in some work prototypes, and it's also my first 'real' open source project, so comments & feedback would be great!




Nice work, love the use of reactivity via observables and pub/sub stores (some of my favourite abstractions)

Some feedback from first glance is “type inference” could be better, which would help prevent a lot of foot-guns. Consider the example code here:

    this.count = this.observable(0);
Does this allow “count” to be inferred as a “number” type in the rest of the code? There might be a way to allow JS to easily infer the types, without requiring a build step…


This is great feedback, thanks! I think there’s something around JSDoc types that can accommodate this. Something I’ll put as a milestone before v1.


Looks good! FWIW I always felt the observable pattern much more intuitive than the redux/reducer style. Something like https://mobx.js.org/

Things get hairy in both, but redux pattern feels so ridiculously ceremonial all to effectively manage a huge global state object with a false sense of "purity".

Observables otoh say "fuck it, I'm mutating everything, do what you want with it".


Thanks! Cami is technically a combination of both. I like the intuitiveness of observables so that’s used:

```

// define observable

this.count = this.observable(0);

// getting

this.count.value;

// setting

this.count.update(value => value + 1);

```

But it also has a redux-like pattern where you can dispatch actions and register reducers with far less ceremony:

```

// define store

const todoStore = createStore({ todos: [], });

// register reducer / producer

todoStore.register('add', (store, payload) => { store.todos.push(payload); });

// subscribe component to store

this.todos = this.subscribe(todoStore, 'todos');

// dispatch

this.dispatch("add", newTodo);

```

It looks like it’s mutating, but both the reducers and update() uses immer* under the hood, so we still respect immutability under the hood.

Cami supports redux devtools so you can use that for time-travel debugging too.

—-

* https://github.com/immerjs/immer


> this.dispatch("add", newTodo);

Will the V8 JIT optimize the hash based function table loop into a constant integer array lookup?


Great to see the interest growing around native Web Components. I've been working on a no-code (markup only) platform which is also based on web components.

https://saasufy.com/

I built a whole chat app with authentication, blockchain and GitHub login with just 120 lines of HTML markup no front end or backend code (all the logic is abstracted away via generic, easy-to-use components provided by the platform).

You can also build apps which display filtered views by category or with text search with custom parameters and it does so efficiently with indexing and all updates in real time. Real time updates are delivered efficiently only to relevant components. Components automatically subscribe and unsubscribe to and from realtime changefeeds in an efficient way. Access control can be specified at the object/model level or on individual fields.


lit-html author here. I'm glad the template library is useful for you!

Question regarding interop with other web components: I don't see how to create reactive properties on elements. Can they receive data from parents via property bindings? Ie, could a Lit element pass data to a Cami element?


Yes, the lib's great!

Unfortunately I haven't thought much yet about interoperability with other web components libraries like lit. I imagined folks would choose just one web component library over the other.

That said, you can initialize reactive properties(1), but property bindings won't work if there's a parent LitElement (as my reactive properties need to be called with either a .value method or an .update method for getting and setting respectively).

As of the moment, what's possible is interop with other cami elements using a store, and in a future version, i'm considering a richer event system for external javascript code to listen to.

---

(1) Initializing is possible with observerableAttr: https://github.com/kennyfrc/cami.js/blob/master/examples/008...


Great work! This is such a great concept that slots in nicely between HTMX/Unpoly/AlpineJS and React/Vue/etc.


Thank you! That’s exactly the segment I was going for.


Nice work! I have a no-dependencies project with somewhat similar goals in frameable/el[0], which takes more inspiration from Vue in its interface.

WebComponents give us so much, but just not quite enough to be usable on their own without a little bit more on top. Reactivity with observable store and templating with event binding are the big missing pieces.

[0]: https://github.com/frameable/el


This seems like a great candidate for the few instances in my HTMX projects when it'd be a pain in the ass to write endpoints that enumerate every possible client-side state. I would love to see somebody put the two together and report on how they get along.


I like it a lot! I think I will give this a try on a project I'm working on.


I see many points in common with https://www.arrow-js.com/ Probably that focus on components could be the only difference


Interesting! Many points are indeed similar. Apart from components, Cami also has immutability built-in and state management.


I also created a webcomponent-based framework two months ago: https://realm.codes


The website renders weirdly, there is lots of jumping elements before everything is loaded properly. I guess irs the js rendering?


How does this compare with https://github.com/preactjs/preact?


Seems like Runes (Svelte 5).

Does this support SSR for custom component ?


Yes, Cami uses fine-grained reactivity, so it’s a cousin of similar solutions like Solid signals, Svelte runes, Knockout / and MobX observables.

It doesn’t support SSR as it aims to be backend-agnostic (i.e. you can use python/ruby/haskell, and you can copy+paste the Cami module with no build step and you can start using it).

If you want SSR for the SEO benefits, I think it’s better to render the text-heavy parts as normal HTML for indexing with Google, and then mount the interactive parts with Cami / web components (i.e. “islands architecture”: https://www.patterns.dev/posts/islands-architecture)


Not the author, but on a quick read, everything here is straight-up web standards so there is nothing to server-side render.


this seems pretty cool - I love me some `lit-html`. How do you feel about Cami versus straight up Lit?


I use pure no-build lit for the "interactive islands" the repo describes. Nice to see others have the same idea even if we're not taking the same approach.


You can consider Cami as the light dom sibling of Lit (which uses shadow dom).

Cami loses out on slots & style encapsulation, but you can style Cami components with normal / global css like it’s part of the normal dom. And since there’s no shadow dom overhead, it’s more performant and there is no FOUC if you load CSS in <head>.


I just nope out of the Shadow dom in lit to get external css using createRenderRoot. Mentioned here https://stackoverflow.com/questions/55400222/how-do-you-use-...


Wondering the same thing. What is the value proposition versus using other popular web component libraries like Lit?


This is great, I am a big fan of vanilla.js for personal projects.

Thanks for sharing.


Umm, how do the templates escape malicious input (XSS)?


It seems to be based on lit-html, so anything that's interpolated gets automatically sanitised unless you're using a directive[1] or interpolate other templates created using the "html" function.

[1]: https://lit.dev/docs/api/directives/#unsafeHTML


I've mainly designed this for backend devs (i.e. rails, django people who just render plain html/css on the front-end), and we generally do HTML sanitization on the server side.

Example: https://api.rubyonrails.org/classes/ActionView/Helpers/Sanit...


What's the advantage of not having any build steps? For me personally, it's an instant turn-off, as I'll never use an untyped library.


No-build doesn't mean untyped.

You can typecheck pure js files using JSDoc.

https://www.typescriptlang.org/docs/handbook/jsdoc-supported...


faster and simpler deployment, e.g., rsync to your webserver, no need to wait for the build step.


The Zen of plain script tags.




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: