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.).
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.
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.
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.
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!
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.
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.
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.
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.
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.
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.
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.
Can you give a quick rundown of when it might make more sense to use Tetra?