axum-admin
A modern admin dashboard framework for Axum. Register your entities and get a full CRUD dashboard — search, filtering, pagination, bulk actions, custom actions, and built-in authentication — with zero frontend build step.
Inspired by Django Admin and Laravel Nova.
Features
- CRUD out of the box — list, create, edit, delete for any entity
- Server-side rendering — MiniJinja templates, no JavaScript framework required
- HTMX — partial page swaps for search, pagination, and flash messages
- Alpine.js — embedded, no CDN or build step
- Built-in auth — session-based login with bcrypt passwords; swap in your own auth backend
- Sidebar groups — collapse related entities under named, expandable sections
- Custom icons — Font Awesome icons for the app logo and each entity
- Bulk actions — delete, CSV export, and custom bulk handlers
- Custom actions — per-record actions with confirmation dialogs and HTMX flash responses
- Filters & search — collapsible filter panel, full-text search, column sorting
- Template customization — override any built-in template or load from a directory on disk
- ORM-agnostic — implement
DataAdapterfor any data source - SeaORM adapter — first-party adapter behind the
seaormfeature flag
Quick start
[]
= "0.1"
= { = "1", = ["full"] }
= "0.7"
use ;
async
Open http://localhost:3000/admin/ to see the dashboard. Login with the credentials above.
Sidebar groups
Use EntityGroupAdmin to group related entities under a collapsible sidebar section. Both EntityAdmin and EntityGroupAdmin can be passed to .register().
use ;
new
// ungrouped entity — appears flat in the sidebar
.register
// grouped entities — collapsed under "Blog" in the sidebar
.register
The active group auto-expands when any of its children is the current page.
Custom icons
Set Font Awesome icon classes on the app logo and individual entities:
new
.icon // sidebar logo; default: fa-solid fa-bolt
.icon // sidebar + dashboard card; default: fa-solid fa-layer-group
Implementing DataAdapter
DataAdapter is the bridge between the admin and your database. Implement it for any ORM or data source:
use ;
use async_trait;
use HashMap;
use Value;
;
SeaORM adapter
Enable the seaorm feature and use SeaOrmAdapter<E> with EntityAdmin::from_entity:
[]
= { = "0.1", = ["seaorm"] }
= { = "1", = ["sqlx-postgres", "runtime-tokio-rustls"] }
use SeaOrmAdapter;
.label
.list_display
.search_fields
.adapter
from_entity auto-generates fields from the SeaORM column definitions, including enum Select fields and FK foreign key fields. You can override any field with .field(...).
Field types
| Builder method | HTML input | Notes |
|---|---|---|
Field::text("name") |
<input type="text"> |
|
Field::textarea("body") |
<textarea> |
|
Field::email("email") |
<input type="email"> |
|
Field::password("pass") |
<input type="password"> |
Value not pre-filled on edit |
Field::number("count") |
<input type="number"> |
Integer |
Field::float("price") |
<input type="number"> |
Float |
Field::boolean("active") |
<input type="checkbox"> |
|
Field::date("created_at") |
<input type="date"> |
|
Field::datetime("updated_at") |
<input type="datetime-local"> |
|
Field::select("status", options) |
<select> |
options: Vec<(value, label)> |
Field::foreign_key("category_id", "Category", adapter, "id", "name") |
<select> |
Populated from another entity |
Field::json("metadata") |
<textarea> |
|
Field::custom("slug", widget) |
Custom HTML | Implement Widget trait |
Field modifiers
text
.label // override display label
.required // marks field as required
.readonly // rendered as non-editable in forms
.hidden // omit from all views
.list_only // show in list, hide in form
.form_only // show in form, hide in list
.help_text
Filters and search
List pages include a collapsible filter panel. Filters default to list_display columns; override with filter_fields or provide a custom Field per filter:
.filter_fields
// custom filter widget for a specific column:
.filter
Bulk actions
Bulk delete and bulk CSV export are enabled by default. Disable them or add your own:
.bulk_delete
.bulk_export
.action
Custom actions
Single-record actions appear on the edit page:
builder
.target
.handler
ActionResult variants:
Success(message)— green flash message via HTMX swapError(message)— red flash messageRedirect(url)— HTTP 302 redirect
Lifecycle hooks
.before_save
.after_delete
before_save errors are displayed inline on the form as field-level validation messages. Use __all__ as the key for non-field errors.
Template customization
Override any built-in template by loading a directory — any .html file whose name matches a built-in template takes precedence:
new
.template_dir
Or pass a template string directly (highest precedence):
new
.template
Templates use MiniJinja and support full {% extends %} / {% block %} inheritance:
{% extends "layout.html" %}
{% block content %}
Welcome to {{ admin_title }}
{% for entity in entities %}
{{ entity.label }}
{% endfor %}
{% endblock %}
Built-in templates: layout.html, home.html, list.html, list_table.html, form.html, login.html, flash.html.
Custom authentication
Implement AdminAuth to integrate with your existing user system:
use ;
use async_trait;
;
new.auth
License
MIT