Routing

Rejoice uses file-based routing. Every .rs file in src/routes/ becomes a route automatically.

File-to-URL Mapping

File PathURL
src/routes/index.rs/
src/routes/about.rs/about
src/routes/contact.rs/contact
src/routes/blog/index.rs/blog
src/routes/blog/post.rs/blog/post
src/routes/users/[id].rs/users/:id

Naming Convention

File names with underscores are converted to hyphens in URLs:

FileURL
src/routes/about_us.rs/about-us
src/routes/contact_form.rs/contact-form

Basic Route

Route files export functions named after HTTP methods like get or post:

src/routes/index.rs
use rejoice::{Req, Res, html};

pub async fn get(req: Req, res: Res) -> Res {
    res.html(html! {
        h1 { "Hello, World!" }
    })
}

Index Routes

Files named index.rs handle the directory's root path:

  • src/routes/index.rs/
  • src/routes/blog/index.rs/blog
  • src/routes/users/index.rs/users

Dynamic Routes

Use square brackets for dynamic path segments:

src/routes/users/[id].rs handles /users/:id:

src/routes/users/[id].rs
use rejoice::{Req, Res, html};

pub async fn get(req: Req, res: Res, id: String) -> Res {
    res.html(html! {
        h1 { "User " (id) }
    })
}

The parameter is passed as the last argument to your handler function.

Examples

FileURLParameter
[id].rs/users/123id = "123"
[slug].rs/blog/hello-worldslug = "hello-world"
[category].rs/products/electronicscategory = "electronics"

Route Function Signatures

Stateless Routes

For apps without shared state (using routes!()):

// Basic route
pub async fn get(req: Req, res: Res) -> Res

// Dynamic route
pub async fn get(req: Req, res: Res, id: String) -> Res

Stateful Routes

For apps with shared state (using routes!(AppState)):

// Basic route
pub async fn get(state: AppState, req: Req, res: Res) -> Res

// Dynamic route  
pub async fn get(state: AppState, req: Req, res: Res, id: String) -> Res

Nested Directories

Create nested routes by adding subdirectories:

Next Steps