Library / SDK
tetra-framework/tetra avatar
tetra-framework/tetra

Tetra: a Django component framework where the browser holds resumable server state

Tetra - A full stack component framework for Django using Alpine.js

612 stars23 forksPythonMIT

At a glance

What is it?
Tetra is a full stack component framework for Django that pairs a server side Python component with Alpine.js in the browser, keeping the component's encrypted server state between calls so that a public method can update the rendered component in place. It suits a Django team that wants component style UI without a JavaScript build pipeline, and it is an alpha project whose last release was v0.1.1 in 2024.
Who is it for?
Tetra fits a Django team that wants components, not pages, and that is willing to put its front end logic in Python with a little Alpine.js rather than in a separate JavaScript application. It does not fit a team that wants a mature, widely adopted component system, since the project is marked as an alpha in its own metadata and the release history stops at v0.1.1 in April 2024.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 21 days ago.
What is it written in?
Mainly Python, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.

Editorial analysis

The state travels in both directions, and the server half is encrypted

Tetra is a new full stack component framework for Django, bridging the gap between your server logic and front end presentation. It uses a public shared state and a resumable server state to enable inplace updates for components, and it encapsulates your Python, HTML, JavaScript and CSS into one file or directory for proximity of related concerns.

That resumable server state is the interesting mechanism, and it is the part that differs from most Django front end arrangements. The components' full server state is saved between public method calls, and this state is encrypted for security.

The reason it has to be saved is the shape of the interaction. A method marked public can be called from JavaScript in the browser, and when it is, the server resumes the component's state rather than starting from a fresh instance, so the component behaves like an object that persists across a call rather than like a view function that is re-entered.

The encryption is the part that deserves attention. The state is server side logic that continues to run in the browser's name, so it is serialised, sent to the client and sent back, and if it were readable a client could tamper with it. The pyproject file lists cryptography at 37.0.1 or later, which is where the encryption comes from.

Alongside that, attributes can be decorated to indicate they should be available in the browser as Alpine.js data objects. That is the public shared state, and the pairing of a public attribute and a public method is the whole programming model: the attribute is the browser's view of the component, the method is the action it can invoke.

The primary language is Python, the licence is MIT, the default branch is main, and the last push was on 2026-10-02. The homepage is tetraframework.com and the documentation is on readthedocs.

A component with its HTML, its CSS and its Alpine.js in one Python file

The example in the README is a single class with a template string attached, and it shows the encapsulation claim in full.

python
from tetra import Component, public

class InfoCard(Component):
    title: str = "I'm so excited!"
    content: str = "We got some news for you."
    name: str = public("")

    @public
    def close(self):
        self.client._removeComponent()

    @public
    def done(self):
        print("User clicked on OK, username:", self.name)
        self.content = f"Hi { self.name }! No further news."

The mechanics of that snippet are worth reading closely. The name attribute is declared as public, so it is shared with the browser and bound by the x-model attribute on the input, and the done method is public, so the button's click handler can invoke it. When the method assigns to self.content, the browser sees the change, and the README says the whole thing is rendered in your template with just a template tag, with no page reload needed.

The template attribute holds the markup, and it is a Django template rather than a string of HTML, because the component can interpolate a live variable with a template tag, as the template does with the name field. That is what makes the browser and the server agree on the same data.

The wider claim is in the What does Tetra do exactly section. Component encapsulation: each component combines its Python, HTML, CSS and JavaScript in one place for proximity of related code. Resumable server state: the components' full server state is saved between public method calls, and this state is encrypted for security. Public server methods: methods can be made public, allowing you to easily call them from JS on the front end, resuming the component's state. Shared public state: attributes can be decorated to indicate they should be available in the browser as Alpine.js data objects. Server watcher methods: public methods can be instructed to watch a public attribute, enabling reactive re-rendering on the server. Inplace updating from the server: server methods can update the rendered component in place, powered by the Alpine.js morph plugin.

Watcher methods, the morph plugin, and components as libraries

The server watcher method is the feature that makes Tetra more than a form helper, and it is the least obvious of the group.

Public methods can be instructed to watch a public attribute, and that enables reactive re-rendering on the server. So when a public attribute changes, the server recomputes the component's rendering, and the update is applied in place, powered by the Alpine.js morph plugin.

The distinction is worth holding onto. In a typical Alpine.js application, the reactivity lives in the browser and the server only serves data. In Tetra, the component's rendering logic is Python that runs on the server, and the browser is told what changed. That is the design that lets a component be written by a Python developer who does not want a separate front end build, and it is also why the state has to be serialised and encrypted.

The morph plugin matters because of how the update is applied. Inplace updating from the server means the rendered component is patched rather than the page reloaded, and the plugin handles the DOM diffing so the browser keeps its scroll position, its focus and its input state.

Component library packaging is the other structural feature. Every component belongs to a library, and their JavaScript and CSS is packed together for quicker browser downloads. So a project that has a hundred components ships a handful of bundled assets rather than a hundred separate files, which is the difference between a component system that works on a real page and one that does not.

Components with overridable slots close the loop on reuse. Components can have multiple slot tags which can be overridden when used, which is the mechanism that lets a generic card component accept different content in different places without duplicating it.

The remaining two features are about the build. JS and CSS builds use esbuild, both for development, built into runserver, and for production. And automatic JavaScript source maps are generated during development so that you can track down errors to the original Python files, which is a small thing that saves a lot of time when a component throws.

Forms that can change their own fields, and a different model from Django

The forms feature is where Tetra diverges most visibly from Django's own conventions.

Form components can act as simple replacements for Django's FormView, but due to Tetra's dynamic nature, a field can for example change its value or disappear depending on other fields' values.

That is a genuine improvement over a static form, where a field that should only appear when a checkbox is ticked needs a round trip to the server and a re-render of the whole form. In Tetra, the decision can live in the same Python class as the field logic, and the update is applied in place through the morph plugin.

It also changes the mental model, because a Django FormView is a request and response cycle and its field behaviour is determined before the response is generated. A Tetra form component is a live object whose fields respond to each other, and a developer coming from Django templates has to think in terms of that object rather than in terms of a submitted form.

The syntax highlighting with type annotations feature is the other piece that makes the single file idea pleasant rather than painful. Tetra uses type annotations to syntax highlight your JavaScript, CSS and HTML in your Python files, with a Visual Studio Code plugin to do it. So a template string with Alpine.js directives inside a Python file gets coloured as JavaScript rather than as an untyped string, which is what makes it bearable to keep the front end markup inside the Python source.

The project metadata also records the practical constraints. It requires Python 3.12 or later, Django 5.2.3 or later, and it is classified as an alpha. The dependency list is short, with cryptography, django, python-dateutil, sourcetypes and pydantic.

There is also a package rename in the history, which is worth knowing if you are following the project. The release notes show v0.0.5 as the last version of the tetraframework package, with the name changed to tetra, and v0.0.6 as the transition package to tetra, followed by v0.1.1 in April 2024.

Where it sits against a Django template and against a JavaScript front end

Two comparisons are worth making, because Tetra sits between two established approaches.

Against plain Django templates, the difference is state and update granularity. A Django view renders a page, the browser receives HTML, and any change to the page is a new request. Tetra keeps a component alive, so a public method can change one part of the output while the rest of the page stays put. For a dashboard or a form with dependent fields, that is the difference between a page reload and a local update.

Against a JavaScript front end with a build pipeline, the difference is where the logic lives and what you have to learn. A modern front end puts the view logic in JavaScript, with a bundler, a package manager and usually a framework, and the server sends JSON. Tetra puts the view logic in Python, sends HTML, and uses Alpine.js for the browser side. The build tooling disappears, and so does the browser dev tooling ecosystem, because the front end is now the Python file.

That last point cuts both ways. The upside is a small dependency surface, five packages, and no node_modules. The downside is that the browser side is constrained to what Alpine.js offers and what Tetra exposes, and the typing story is handled by a Visual Studio Code plugin rather than by a type checker, so mistakes in the template are found at runtime.

And the honest third comparison is with a mature component system for a JavaScript ecosystem, where the component libraries, the form libraries and the state management are all separate, well documented and separately versioned. Tetra offers all of that in one Python framework, at the cost of being an alpha project with a single primary author.

For a Django team with no separate front end, the trade is usually worth it. For a team that already has a front end and a build pipeline, Tetra is asking them to give that up, and the README does not pretend otherwise.

Editorial conclusion

Tetra fits a Django team that wants components, not pages, and that is willing to put its front end logic in Python with a little Alpine.js rather than in a separate JavaScript application. It does not fit a team that wants a mature, widely adopted component system, since the project is marked as an alpha in its own metadata and the release history stops at v0.1.1 in April 2024. Before adopting it, read the installation documentation, because the package install is only the first step and Tetra needs customization in the Django project, and test the forms behaviour, because the dynamic nature of the components changes how field values and visibility behave compared with a Django FormView.

Frequently asked questions

How do I install Tetra for Django?

The package installs with uv pip install tetra, but the README notes that this is not all, because Tetra needs some customization in your Django project, with the details in the installation documentation. The project requires Python 3.12 or later and Django 5.2.3 or later.

What is a public attribute and a public method in Tetra?

Attributes decorated as public are made available in the browser as Alpine.js data objects, and methods made public can be called from JavaScript on the front end, resuming the component's server state. A public method can also be instructed to watch a public attribute, which enables reactive re-rendering on the server.

How does Tetra update a component without a page reload?

Server methods can update the rendered component in place, powered by the Alpine.js morph plugin. The component's full server state is saved between public method calls and that state is encrypted for security.

How do Tetra forms differ from a Django FormView?

Form components can act as simple replacements for Django's FormView, but because of Tetra's dynamic nature a field can change its value or disappear depending on other fields' values. Forms, therefore, behave as live objects rather than as a request and response cycle.

Official sources

  1. License: MIT
  2. Project website
  3. README
  4. Releases
  5. tetra-framework/tetra on GitHub
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/tetra-framework-tetra.svg)](https://hysenlabs.com/projects/tetra-framework-tetra)

Was this useful?