A Unified Approach
Stately is a framework for building Rust backends and TypeScript/React frontends, with an emphasis on configuration-driven applications. It provides a unified approach to state management, API generation, and UI rendering that spans your entire stack.
The Problem
Building full-stack applications often involves significant boilerplate:
- Defining data models in multiple places (backend structs, API schemas, frontend types)
- Writing CRUD endpoints by hand for every entity, often inconsistent and ad-hoc
- Building forms that match your data structures
- Keeping everything in sync as your application evolves
- Designing around denotational semantics for scalability and composability
Changes to your data model cascade through layers of code, each requiring manual updates. Poor decisions up front can escalate problems later on as your application's capabilities grow.
In more cases than I can count, I needed to build an application that a user could install themself. The common method was to provide a configuration file argument that users could supply a path to a local file which would change the way the application behaved. Over time I realized providing users with a simple UI they could run and access locally was a much better experience. What I needed was a framework I could leverage to cut down on the boilerplate inherent to these types of applications. This is just one use case where state and configuration align. In reality, there are countless possible.
The Stately Approach
Stately takes a different approach: define your entities, derive everything else.
On the backend, you define your entities as Rust structs with derive macros, and compose your state:
From this definition, Stately generates:
- Type-safe state management supporting both
collectionsandsingletons - Complete CRUD API endpoints through the unified state structure
- OpenAPI documentation
- Serialization/Deserialization and validation
On the frontend, you consume the generated OpenAPI spec:
This gives you:
- Fully typed API client, accessible anywhere in the application
- Schema-driven form rendering
- React hooks for data fetching and mutations
- Automatic page layouts and app navigation
- Pre-built views and pages
- Access to pre-defined or app-defined plugins
Key Concepts
Entities
Entities are the core data structures in your application. They have friendly names, can be referenced by ID, and are managed in collections.
State
State is a container for your entity collections. The #[stately::state] macro generates the infrastructure for managing entities:
Links
Links represent relationships between entities. They can be references (by ID) or inline (embedded):
API
Stately currently supports axum (support for different libraries planned) and provides a simple way to automatically construct everything needed to manage the Stately state over api endpoints:
Plugins
Plugins extend Stately with additional capabilities. Each plugin provides coordinated backend and frontend packages.
Currently Stately provides two plugins out of the box.
Files
File upload, automatic versioning using time-sortable unique ids, and file management.
Implement axum::extract::FromRef from FileState to your defined and annotated api state, and simply include the file plugin's routes in your root router:
Arrow
Data connectivity with SQL query execution, powered by the battle-tested Datafusion.
Implement axum::extract::FromRef from QueryState to your defined and annotated api state
When to Use Stately
Stately is a good fit when:
- You're building a configuration-heavy application
- Entity-driven or concept-driven design guides your app development
- Denotational design provides entities and their relationships up front
- You want type safety across your full stack
- You need CRUD operations across different entity types
- You want schema-driven UIs that stay in sync with your backend
- You plan to extend functionality with plugins
- You want a consistent framework that AI can understand and work with. learn more
Stately may not be the best choice when:
- Your API is primarily highly custom designs that don't follow CRUD patterns
- Your frontend has complex state management needs that don't align with your backend
- You're building ad-hoc applications where boilerplate or cross-API consistency is not needed
Architecture Overview
Plugins are vertical slices of functionality, spanning frontend to backend: