Rejoice
A simple and delightful web framework for Rust with file-based routing, layouts, live reload, Tailwind CSS, and SolidJS islands for interactivity.
Features
- File-based routing - Just create
.rsfiles insrc/routes/and they become pages - Nested layouts - Share UI across routes with
layout.rsfiles - Live reload - Changes automatically refresh the browser
- Tailwind CSS v4 - Utility-first CSS that scans your Rust and TSX files
- SolidJS islands - Add interactive components without a full SPA
- Type-safe HTML - Use Maud for compile-time HTML templating
- SQLite database - Optional built-in support with sqlx
- Zero config - Just run
rejoice devand start building
Quick Start
# Install the CLI
# Create a new project
# Start the dev server
To create a project with SQLite database support:
File-Based Routing
src/routes/
├── layout.rs -> Wraps all pages
├── index.rs -> GET /
├── about.rs -> GET /about
└── users/
├── layout.rs -> Wraps /users/* pages
├── index.rs -> GET /users
└── [id].rs -> GET /users/:id
Each route file exports a page function:
use ;
pub async
Layouts
Layouts wrap pages and nested layouts. Create a layout.rs file to share UI:
use ;
pub async
Layouts nest automatically. A page at /users/123 will be wrapped by:
routes/layout.rs(if exists)routes/users/layout.rs(if exists)routes/users/[id].rs
Database Support
Create a project with --with-db to get SQLite support out of the box:
This sets up:
- A SQLite database file
.envwithDATABASE_URL- An
AppStatestruct with a connection pool - Routes configured to receive state
Access the database in your routes:
use crateAppState;
use ;
pub async
Custom App State
You can add your own state (database, config, services, etc.) to make it available in all routes:
use ;
routes!;
async
Then access it in routes and layouts:
pub async
SolidJS Islands
Add interactive components to your pages:
use ;
pub async
Create the component in client/Counter.tsx:
import { createSignal } from "solid-js";
export default function Counter(props: { initial: number }) {
const [count, setCount] = createSignal(props.initial);
return (
<button onClick={() => setCount((c) => c + 1)}>
Count: {count()}
</button>
);
}
That's it! The island is automatically registered and hydrated on the client.
Tailwind CSS
Tailwind CSS v4 is included out of the box. Just use Tailwind classes in your Rust templates or TSX components:
use ;
pub async
Tailwind automatically scans your src/**/*.rs and client/**/*.tsx files for classes.