Hacker Newsnew | past | comments | ask | show | jobs | submitlogin
Tetra – Full stack reactive component framework for Django using Alpine.js (tetraframework.com)
126 points by samwillis on May 27, 2022 | hide | past | favorite | 47 comments


I've been using django/alpine/tailwind/htmx together lately, and I really like it.

Can you give a quick rundown of when it might make more sense to use Tetra?


So in that stack Tetra would replace htmx (which is an awesome tool!) and takes control of your Alpine components.

Tetra provides the "glue" between Django and Alpine, but also encapsulates your components so all html/css/python/js thats related is next to each other. You wouldn't need to be writing views that return html fragments for htmx.

One of its big features though is that your server rendered views can be reactive, re-rendering on the server and updating in the browser based on user interactions a client side state changes. You have to write less glue code to make everything work with Tetra.


This looks really great! Can you mention some of the differences between Tetra and Unpoly? The full page diff approach Unpoly uses matches my mental model (and follows Django's natural request/response cycle). It sounds like Tetra does something similar?

In my tests, HTMX takes about half the time to render vs Unpoly. I assume HTMX is a lot faster than Unpoly because it's only processing fragments versus a full-page diff. How does the rendering speed with HTMX vs. Tetra's stack up if you've done any testing?


I'm not as familiar with Unpoly so this is based on limited knowledge. I think Unpoly is more based the concept of "frames" that can be changed dynamically, so in some ways it is very similar to HTMX with it's updating fragments. But I'm assuming it is diffing the DOM rather than just replacing it as HTMX does.

Tetra on the other hand is very "component" focused, everything is built up out of nested components. These are rendered on the server and a diff/merge is performed by the Alpine.js Morph plugin to update the dom. So Tetra doesn't re-render the whole page, only the component that changes, therefore it probably sits somewhere between Unpoly and HTMX, but I have done no benchmarking.

Tetra is also tightly linked to Python, each components server state is "frozen" and sent to the browser when it first renders (encrypted and signed). If you then call any public methods on the component the state is sent back to the server and "resumed". This makes the component fully statefull between server public method calls. Neither HTMX or Unpoly have this, you have to manually implement the end points for all updates. Tetra does that all for you.

Compared with HTMX and Unpoly you should be writing less code with Tetra.


> I think Unpoly is more based the concept of "frames" that can be changed dynamically, so in some ways it is very similar to HTMX with it's updating fragments. But I'm assuming it is diffing the DOM rather than just replacing it as HTMX does.

That's fascinating, thanks for sharing. I will have to play around with Tetra. Based on your description, it certainly feels like, "Greetings. We are from the future." :-)

My experience with HTMX and Unpoly is limited to building three prototypes (once each in HTMX and Unpoly). Based on that:

* For Unpoly, you mark HTML elements that you want Unpoly to monitor/manipulate. Then some event happens (e.g. the user submits a form, clicks a link, etc.).

* Then Django returns the entire page, just like an old school, web 1.0 app.

* Finally, Unpoly looks at the entire page and finds the elements it's told to monitor/change. Then it swaps those elements in the DOM (if needed), which prevents a page reload.

You should look at Unpoly to see if it sparks any ideas. Its layers feature [0] in particular is really awesome. In the link below, it shows how to (from a high level) use layers to build something pretty complicated like Gmail, while keeping the back end code relatively simple using any SSR framework (Django, Rails, etc.).

[0] https://unpoly.com/tutorial


Just curious, why would you use both alpine and htmx? Do you use htmx for simple stuff, and alpine for more complex things?


Htmx isn’t built for interactivity like opening a menu. That is where alpine comes in.


React may have convinced the community to put HTML in their JavaScript, but I'm extremely reluctant to put JavaScript in my Python.


I completely understand the hesitation, it appears to be going against 30 years of separation of concerns. However, there is a big difference between the HTML/JS mix in React, there you are using JS as a template language, mixing it with your html. Personally I prefer a domain specific template language (like Django or Vue.js) as it enforces separation of concerns. With Tetra you aren't mixing your js/html/css/python, they are just conveniently located next to each other. It's much closer to the idea of Vue.js single file components, where you have your JS, CSS, and HTML for a component all in the same file.

I have been calling what Tetra does "close proximity of related concerns", the creator of HTMX called it "locality of behaviour" (https://htmx.org/essays/locality-of-behaviour/).

The original idea with Tetra was to use Python import hooks to create something akin to Vue.js single file components, and I did in fact built an early prototype doing that. However then the idea came to use Python type annotations to enable you to indicate to your editor to syntax highlight inline strings (the Vue.js like single file components would have needed an even more complex editor plugin and setup).

So, it's not going to be to everyone's taste, it it shouldn't be. There are a million ways to build a website, this is just another. However, I believe its good way to do it.

It's worth noting that it's possible to split the html/css/js out into other files if you set your template to '{% extend "template.html" %}', your styles to '@import "styles.css";", and your script to 'export default from "script.js"'.


I did something similar in Django (ish) where I defined CSS, JS and Django templates in one file using special blocks then pre-processed them into vanilla templates, own JS and CSS. It worked well but was a sketch of what Tetra is doing. I certainly think there's a need for full stack the Django way (tm)


> It also encapsulates your Python, HTML, JavaScript and CSS into one file.

After years and years of hearing people much more knowledgeable than recommend seperation of concerns and me actually following it, I find this "feature" horrifying.

So my honest question to you, people who used similar frameworks, is this really helpful? How is this not confusing and not a source of undebuggable code?


I have a blog post in the works to go into depth the thoughts about this.

The gist though (copied from the introduction):

--

Proximity of related concerns is as important as separation of concerns. Whilst it is important to keep your backend logic, front end JavaScript, HTML, and styles separate, it is also incredibly useful to have related code in close proximity.

Front end toolkits such as Vue.js, with its single file components and newer "utility class" based CSS frameworks such as Tailwind, have shown that keeping related aspects of a component in the same file helps to reduce code rot, and to improve the speed at which developers gain an understanding of the component.

--

Really the intention is that you can keep small parts of CSS/JS close to where it is being used. Larger functionality, particularly anything reused, you would still break out into separate files.

The creator of htmx refers to this as "Locality of Behaviour", they wrote a good post on it (https://htmx.org/essays/locality-of-behaviour/)


Separation of concerns is a really important concept, but I also think it's very often misinterpreted.

The real question is where are the inherent couplings within a system? In places where they exist, collocation is a very good thing. Separation of concerns is best applied parts of the system with low inherent coupling.

In the case of web UI, the controller logic, document structure (HTML), presentation (CSS), and client-side interaction logic (JS) often have very tight inherent coupling, to the point of needing to relate to each other through shared identifiers (e.g. DOM classes, variables). Collocation generally allows you to scrap boilerplate declarations and work on an entire UI component in one screen of source code, rather than bouncing between tightly coupled files. In my experience, this is a huge productivity boost and aid in understanding code other people wrote.


If you take a "concern" to be domain-defined rather than architecture-defined, then this approach separates concerns effectively.


> After years and years of hearing people much more knowledgeable than recommend seperation of concerns

You should never ever just trust experts or popular advice at face value. You have to test it out.

If you are not doing a large project with many people, you certainly do not need to have files all over the place.


This looks incredible. Great choice for using alpine. This matches my stack perfectly, will definitely give it a whirl. I expect I’ll end up using tetra for a long while yet. Congrats and thank you for such a great contribution to the community!


As someone who always throw away web side projects when it's time to do frontend, this looks perfect lol

Thanks for sharing !


Thanks, I have big plans for it!


Looks great. Just a heads up that there a bug in the example at https://www.tetraframework.com/docs/introduction

As it stands you'll get

  TypeError: ToDoItem.save() takes 3 positional arguments but 4 were given
when trying to add a ToDo.


Thanks, there was an error in the docs, public methods that are watching an attribute should be `def name(self, value, old_value, attr)`. "attr" is the name of the attribute that has changed. Fixed!


While I'm suggesting changes, you should probably make it clear that you have to link in bootstrap and font-awesome explicitly in your template to make all the styling work. I initially assumed that it was somehow included in tetra and it took me a second to work out why all the styling was missing.


Oh that’s just for the docs and demo site, no need for bootstrap or font-awesome at all. You can use whatever you want.


Yea, I got that. But if someone is just following along the demo in the docs they won't get the results they'll be expecting unless they add bootstrap and font-awesome. It's probably worth mentioning that they should add the relevant imports to the template index.html for the demo.


I love these kinds of projects. I hope you'll have the time you need and enough attention/traction to make it work.

I'm not sure it's possible, but it could be great to make this a team project officially supported by Django (like DRF or channels). This would bring trust and support, compared to solo-dev projects that can die instantely if the developer can't support it anymore. Anyway, I'd be glad to sponsor your project on Github.

From this new perspective, here are the tools I'd use in my front-end Django: - Level 0: no dynamic UI needs -> no additional tool (pure HTML/CSS templates) - Level 1: simple dynamic needs -> HTMX + Hyperscript - Level 2: SPA-like needs -> Tetra (instead of Alpine itself)


I agree with some of the other early sentiments, this is interesting. We just recently started integrating React into our Django projects. Are there any plans to allow for different JS frameworks, or does the approach tightly couple with Alpine.js?

I may have to play around with this regardless; very cool.


Currently yes, it is tightly coupled with Alpine.js for the server<->browser coms. However Alpine is so lightweight it's easy to throw in an `x-ignore` [0] and then mount any other framework for a component and still take advantage of the coms and bundling.

There is a "BasicComponent" that currently has no JS, one idea is expand this with very basic js support (when it mounts on the page your js would receive the element) so that other frameworks can be used with Tetra, probably implemented as extensions.

For react you should take a look at Reactivated[1] though! they are doing interesting things with Django and React.

0: https://alpinejs.dev/directives/ignore

1: https://www.reactivated.io


Looks cool! My company uses Django & Alpine.js so I will be keeping my eye on this. Would Tetra replace something like HTMX (htmx.org) or would it be used in conjunction with it?


Yes, you wouldn't need HTMX (which is awesome!) with Tetra. The idea is that Tetra is an opinionated framework (with escape hatches), it will come with everything you need.


samwillis: since I see you are here answering questions, any reason you choose Alpine over HTMX?

I've been thinking about building something like Tetra for Flask, but was planning on using HTMX.


Firstly HTMX is awesome, its a really cool project. There is also a Django project implementing helpers for it: https://django-htmx.readthedocs.io/en/latest/

HTMX and Alpine take quite different approaches, HTMX is more about calling remote endpoints, returning html fragments and replacing parts of the DOM. It's really nicely done.

Alpine on the other hand is more about decorating your elements with directives and having a "reactive" client side state, very much like a mini Vue.js (it actually uses the reactive core from Vue). It also has a "morph" plugin, which Tetra uses, that rather than replacing the dom with html retuned from the server it makes all the necessary changes to make the existing dom match, keeping all user state (cursers in text boxes etc.). This is a lot like how Vue and React work.

Tetra components are sort of inspired by Vue and so more closely align with the Alpine approach.


Thanks for the detailed reply. It's helpful to understand the difference between the two approaches at a high level.

You probably know this but for others who might read, HTMX has an alpine plugin which uses morph and persists client side state, including Alpine competent state: https://htmx.org/extensions/alpine-morph/

I also thought HTMX persisted some state on swapped elements, but it's possible that's only the html attributes. Can't seem to find details in the docs at the moment.


I was hacking together a Django component system (non reactive) and this looks really cool. I love the approach and think Django should come with this kind of thing out of the box in 2022. I'll be using it for sure.


seems a bit slow, like that movie search example. Using normal frontend framework the response is instant as I type. This seems to be half a second delay on each keystroke.


There is a 200ms debounce on that example, it's also on a tiny DigitalOcean droplet. (I will reduce that debounce)

The approach is similar to Laravel Livewire, and so it's possible to have the same level of responsiveness.


The code seems to suggest they have a 200ms throttle between each keystroke, which should explain the delay.


i makes sense!


How does graceful degradation work for Tetra? Eg how would a non-js user experience the presentation of tetra components?


Really interesting, are you using it in production now? I would love too just not sure if its stable enough yet?


Not yet, but soon. I would call it an Alpha right now, but plan to release V1 this summer at which point the API will have stabilised and be well tested.


Ah cool, maybe i'll give it a go on a dummy project but it seems really useful. Is there a roadmap for features or timeline?


Bottom of the homepage has a list of what's planed. No specific timeline other than a V1 release this summer.

Keen to get any feedback, I think there is gap in the Django ecosystem that this fills and want to build something thats super useful to the community.


HTML & CSS in Python is ugly. Is there a way to split those from python ?


Yes, you can just set your template to '{% extend "template.html" %}', your styles to '@import "styles.css";", and your script to 'export default from "script.js"'.

However I do think for the small sort of sprinkling of CSS/JS that is common for components you will find that actually having them embedded within the component file an advantage.


That 5-line description alone freezes my brain.


Ha, yes, still working out the best way to describe it.


Like the LiveView for Phoenix


Someone should make a family tree of frameworks. Tetra is the child of LiveWire, is the grandchild of LiveView, is the cousin of …




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

Search: