Skip to main content
The NoContent response type returns a 204 No Content status code, indicating that the request succeeded but there’s no content to return.

Basic usage

Return NoContent from handlers that perform an action but have nothing to return:
use axum::{
    extract::Path,
    response::NoContent,
    routing::delete,
    Router,
};

async fn delete_user(Path(user_id): Path<String>) -> Result<NoContent, String> {
    // Delete user from database
    database::delete_user(&user_id).await?;
    Ok(NoContent)
}

let app = Router::new().route("/users/{user_id}", delete(delete_user));

When to use NoContent

DELETE operations

The most common use case is successful DELETE requests:
use axum::{
    extract::{Path, State},
    http::StatusCode,
    response::IntoResponse,
};
use uuid::Uuid;

async fn todos_delete(
    Path(id): Path<Uuid>,
    State(db): State<Db>,
) -> impl IntoResponse {
    if db.write().unwrap().remove(&id).is_some() {
        StatusCode::NO_CONTENT
    } else {
        StatusCode::NOT_FOUND
    }
}
Example from examples/todos/src/main.rs:146-151.

PUT operations with no response body

When updating a resource without returning the updated data:
use axum::{
    extract::{Path, Json},
    response::NoContent,
};

async fn update_settings(
    Path(user_id): Path<String>,
    Json(settings): Json<Settings>,
) -> NoContent {
    save_settings(&user_id, settings).await;
    NoContent
}

Successful operations with no data

Any operation that succeeds but has nothing to return:
use axum::response::NoContent;

async fn mark_as_read(message_id: String) -> NoContent {
    mark_message_read(&message_id).await;
    NoContent
}

How it works

Type definition

pub struct NoContent;
See definition in axum/src/response/mod.rs:78.

IntoResponse implementation

The NoContent type simply converts to a 204 No Content status code:
impl IntoResponse for NoContent {
    fn into_response(self) -> Response {
        StatusCode::NO_CONTENT.into_response()
    }
}
See implementation in axum/src/response/mod.rs:80-84.

Comparison with unit type ()

The unit type () returns 200 OK with an empty body, while NoContent returns 204 No Content.

Using () returns 200 OK

async fn handler() -> () {
    // Returns: 200 OK with empty body
    ()
}

Using NoContent returns 204 No Content

use axum::response::NoContent;

async fn handler() -> NoContent {
    // Returns: 204 No Content
    NoContent
}

When to choose which

  • Use NoContent (204): When the operation succeeded and there’s intentionally no content (DELETE, some PUTs)
  • Use () (200): When you want a default successful response (less common)
  • Use StatusCode::NO_CONTENT: When you need more control or want to return it conditionally

Response fields

status
StatusCode
Always 204 No Content
body
empty
The response body is always empty

Common patterns

Using with Result for error handling

use axum::{
    extract::Path,
    http::StatusCode,
    response::NoContent,
};

async fn delete_item(Path(id): Path<u64>) -> Result<NoContent, StatusCode> {
    database::delete(id)
        .await
        .map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?;
    
    Ok(NoContent)
}

Conditional response with status codes

use axum::{
    extract::Path,
    http::StatusCode,
    response::IntoResponse,
};

async fn delete_resource(Path(id): Path<String>) -> impl IntoResponse {
    match database::delete(&id).await {
        Ok(true) => StatusCode::NO_CONTENT,
        Ok(false) => StatusCode::NOT_FOUND,
        Err(_) => StatusCode::INTERNAL_SERVER_ERROR,
    }
}

Using StatusCode directly

You can also use StatusCode::NO_CONTENT directly:
use axum::http::StatusCode;

async fn handler() -> StatusCode {
    // Equivalent to returning NoContent
    StatusCode::NO_CONTENT
}
Both approaches are valid. NoContent is more self-documenting, while StatusCode::NO_CONTENT gives you more flexibility for conditional logic.

See also