Rust 2026-06-29·Ievgenii Svyryd

Rustango CMS: A Wagtail-Style CMS in Rust

A content management system built on Rustango that ports Wagtail's page tree, StreamField, snippets, and image renditions to Rust - multi-tenant, type-safe, and fast.

Rustango CMS: A Wagtail-Style CMS in Rust

Rustango gave me Django in Rust. The natural next question was the one Django developers always ask once the models are in place: where is Wagtail? Wagtail is the content management system that turns a Django project into something a marketing team can actually run - a page tree, rich structured content, a media library, and an editor interface that non-developers love.

Rustango CMS is that layer for Rustango: a Wagtail-style CMS that ports the page tree, StreamField, snippets, and image renditions to Rust - multi-tenant and type-safe from top to bottom.

Pages as typed models

Every page type is a Rust struct that derives both Model and PageType. The struct's fields become the editable form in the admin via field widgets; the page lives in a tree (cms_page) while its typed data lives in an extension table. You declare allowed parents and children, the template, and an icon - and the CMS wires up routing, breadcrumbs, and the editor.

#[derive(Model, PageType, Default, Serialize, Deserialize)]
#[rustango(table = "blog_post_ext", app = "blog")]
#[page_type(
    type_name = "BlogPost", template = "blog_post.html", icon = "article",
    allowed_parents(BlogIndex),
)]
pub struct BlogPost {
    #[rustango(primary_key)]
    pub id: Auto<i64>,
    #[rustango(fk = "cms_page", on = "id", unique)]
    pub page_id: i64,
    #[field(widget = RichText, label = "Intro")]
    pub intro: String,
    #[field(widget = Stream, allowed(heading, paragraph, image, code, quote))]
    pub body: Option<String>,
}

StreamField: structured content, not a soup of HTML

The block engine is Wagtail's best idea: instead of one big rich-text field, content is an ordered stream of typed blocks - a heading, a paragraph, an image, a code sample, a quote, or a custom block you register yourself. Editors compose pages from building blocks; templates render each block with its own partial. The post you are reading is a StreamField.

Snippets, a media library, and real image renditions

Reusable content that is not a page - authors, categories, a site's branding - lives as snippets in a Library, with folders, search, revisions, and translations. Images go through a media library with on-demand renditions: ask for a size and format and the CMS resizes, crops to a focal point, and re-encodes to WebP or AVIF, caching the result.

{# Responsive, format-negotiated images straight from a template #}
{{ rcms_picture(media_id=hero, filters=["fill-1200x630"],
                formats=["avif", "webp"], alt=page.title) | safe }}

SEO, i18n, and multi-tenancy come standard

Each page carries SEO and Open Graph fields, and a single template helper emits canonical, description, OG, and Twitter-card tags; a sitemap is served automatically. The admin is fully localised, content can be translated per-locale, and - because it is built on Rustango - a single deployment serves many tenants, each with its own database, pages, theme, and media.

A CMS is a promise to the people who are not engineers: that they can change the site without filing a ticket. Rustango CMS keeps that promise in a language that usually forgets those people exist.

Where it is going

Rustango CMS already runs this blog - the page tree, the StreamField bodies, the generated cover images, the SEO tags, and the per-locale content all come from it. It is the proof that Rust does not have to mean giving up the editor experience that made Django and Wagtail so productive. You can have the ergonomics and the guarantees.

Get in touch →