diff --git a/Cargo.lock b/Cargo.lock
index 9a7dee72..128d8825 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -1392,6 +1392,7 @@ dependencies = [
  "log",
  "once_cell",
  "parking_lot",
+ "rustix 0.38.4",
  "serde",
  "serde_json",
  "slotmap",
diff --git a/book/src/configuration.md b/book/src/configuration.md
index 4c152bf4..eb2cf473 100644
--- a/book/src/configuration.md
+++ b/book/src/configuration.md
@@ -89,7 +89,7 @@ The `[editor.statusline]` key takes the following sub-keys:
 
 | Key           | Description | Default |
 | ---           | ---         | ---     |
-| `left`        | A list of elements aligned to the left of the statusline | `["mode", "spinner", "file-name", "file-modification-indicator"]` |
+| `left`        | A list of elements aligned to the left of the statusline | `["mode", "spinner", "file-name", "read-only-indicator", "file-modification-indicator"]` |
 | `center`      | A list of elements aligned to the middle of the statusline | `[]` |
 | `right`       | A list of elements aligned to the right of the statusline | `["diagnostics", "selections", "register", "position", "file-encoding"]` |
 | `separator`   | The character used to separate elements in the statusline | `"│"` |
@@ -108,6 +108,7 @@ The following statusline elements can be configured:
 | `file-modification-indicator` | The indicator to show whether the file is modified (a `[+]` appears when there are unsaved changes) |
 | `file-encoding` | The encoding of the opened file if it differs from UTF-8 |
 | `file-line-ending` | The file line endings (CRLF or LF) |
+| `read-only-indicator` | An indicator that shows `[readonly]` when a file cannot be written |
 | `total-line-numbers` | The total line numbers of the opened file |
 | `file-type` | The type of the opened file |
 | `diagnostics` | The number of warnings and/or errors |
diff --git a/helix-term/src/commands/typed.rs b/helix-term/src/commands/typed.rs
index 67640f79..5bb9c6c4 100644
--- a/helix-term/src/commands/typed.rs
+++ b/helix-term/src/commands/typed.rs
@@ -672,7 +672,7 @@ pub fn write_all_impl(
             }
             if doc.path().is_none() {
                 if write_scratch {
-                    errors.push("cannot write a buffer without a filename\n");
+                    errors.push("cannot write a buffer without a filename");
                 }
                 return None;
             }
diff --git a/helix-term/src/ui/statusline.rs b/helix-term/src/ui/statusline.rs
index 61fca609..52dd49f9 100644
--- a/helix-term/src/ui/statusline.rs
+++ b/helix-term/src/ui/statusline.rs
@@ -145,6 +145,7 @@ where
         helix_view::editor::StatusLineElement::FileModificationIndicator => {
             render_file_modification_indicator
         }
+        helix_view::editor::StatusLineElement::ReadOnlyIndicator => render_read_only_indicator,
         helix_view::editor::StatusLineElement::FileEncoding => render_file_encoding,
         helix_view::editor::StatusLineElement::FileLineEnding => render_file_line_ending,
         helix_view::editor::StatusLineElement::FileType => render_file_type,
@@ -442,6 +443,19 @@ where
     write(context, title, None);
 }
 
+fn render_read_only_indicator<F>(context: &mut RenderContext, write: F)
+where
+    F: Fn(&mut RenderContext, String, Option<Style>) + Copy,
+{
+    let title = if context.doc.readonly {
+        " [readonly] "
+    } else {
+        ""
+    }
+    .to_string();
+    write(context, title, None);
+}
+
 fn render_file_base_name<F>(context: &mut RenderContext, write: F)
 where
     F: Fn(&mut RenderContext, String, Option<Style>) + Copy,
diff --git a/helix-view/Cargo.toml b/helix-view/Cargo.toml
index 203b5dad..b66739a4 100644
--- a/helix-view/Cargo.toml
+++ b/helix-view/Cargo.toml
@@ -51,6 +51,7 @@ clipboard-win = { version = "4.5", features = ["std"] }
 
 [target.'cfg(unix)'.dependencies]
 libc = "0.2"
+rustix = { version = "0.38", features = ["fs"] }
 
 [dev-dependencies]
 helix-tui = { path = "../helix-tui" }
diff --git a/helix-view/src/document.rs b/helix-view/src/document.rs
index af7e3a7e..7602c9c7 100644
--- a/helix-view/src/document.rs
+++ b/helix-view/src/document.rs
@@ -185,6 +185,8 @@ pub struct Document {
 
     // when document was used for most-recent-used buffer picker
     pub focused_at: std::time::Instant,
+
+    pub readonly: bool,
 }
 
 /// Inlay hints for a single `(Document, View)` combo.
@@ -673,6 +675,7 @@ impl Document {
             config,
             version_control_head: None,
             focused_at: std::time::Instant::now(),
+            readonly: false,
         }
     }
 
@@ -955,6 +958,33 @@ impl Document {
         }
     }
 
+    #[cfg(unix)]
+    // Detect if the file is readonly and change the readonly field if necessary (unix only)
+    pub fn detect_readonly(&mut self) {
+        use rustix::fs::{access, Access};
+        // Allows setting the flag for files the user cannot modify, like root files
+        self.readonly = match &self.path {
+            None => false,
+            Some(p) => access(p, Access::WRITE_OK).is_err(),
+        };
+    }
+
+    #[cfg(not(unix))]
+    // Detect if the file is readonly and change the readonly field if necessary (non-unix os)
+    pub fn detect_readonly(&mut self) {
+        // TODO Use the Windows' function `CreateFileW` to check if a file is readonly
+        // Discussion: https://github.com/helix-editor/helix/pull/7740#issuecomment-1656806459
+        // Vim implementation: https://github.com/vim/vim/blob/4c0089d696b8d1d5dc40568f25ea5738fa5bbffb/src/os_win32.c#L7665
+        // Windows binding: https://microsoft.github.io/windows-docs-rs/doc/windows/Win32/Storage/FileSystem/fn.CreateFileW.html
+        self.readonly = match &self.path {
+            None => false,
+            Some(p) => match std::fs::metadata(p) {
+                Err(_) => false,
+                Ok(metadata) => metadata.permissions().readonly(),
+            },
+        };
+    }
+
     /// Reload the document from its path.
     pub fn reload(
         &mut self,
@@ -969,6 +999,9 @@ impl Document {
             .ok_or_else(|| anyhow!("can't find file to reload from {:?}", self.display_name()))?
             .to_owned();
 
+        // Once we have a valid path we check if its readonly status has changed
+        self.detect_readonly();
+
         let mut file = std::fs::File::open(&path)?;
         let (rope, ..) = from_reader(&mut file, Some(encoding))?;
 
@@ -1018,6 +1051,8 @@ impl Document {
         // and error out when document is saved
         self.path = path;
 
+        self.detect_readonly();
+
         Ok(())
     }
 
diff --git a/helix-view/src/editor.rs b/helix-view/src/editor.rs
index 2152ff9b..113102b6 100644
--- a/helix-view/src/editor.rs
+++ b/helix-view/src/editor.rs
@@ -421,6 +421,7 @@ impl Default for StatusLineConfig {
                 E::Mode,
                 E::Spinner,
                 E::FileName,
+                E::ReadOnlyIndicator,
                 E::FileModificationIndicator,
             ],
             center: vec![],
@@ -473,6 +474,9 @@ pub enum StatusLineElement {
     // The file modification indicator
     FileModificationIndicator,
 
+    /// An indicator that shows `"[readonly]"` when a file cannot be written
+    ReadOnlyIndicator,
+
     /// The file encoding
     FileEncoding,