diff --git a/app/assets/javascripts/discourse/app/components/composer-editor.js b/app/assets/javascripts/discourse/app/components/composer-editor.js
index a50aee09671..d110191c24c 100644
--- a/app/assets/javascripts/discourse/app/components/composer-editor.js
+++ b/app/assets/javascripts/discourse/app/components/composer-editor.js
@@ -73,7 +73,13 @@ export default Component.extend({
     const filename = uploadFilenamePlaceholder
       ? uploadFilenamePlaceholder
       : clipboard;
-    return `[${I18n.t("uploading_filename", { filename })}]() `;
+
+    let placeholder = `[${I18n.t("uploading_filename", { filename })}]()\n`;
+    if (!this._cursorIsOnEmptyLine()) {
+      placeholder = `\n${placeholder}`;
+    }
+
+    return placeholder;
   },
 
   @discourseComputed("composer.requiredCategoryMissing")
@@ -888,6 +894,18 @@ export default Component.extend({
     return element.tagName === "ASIDE" && element.classList.contains("quote");
   },
 
+  _cursorIsOnEmptyLine() {
+    const textArea = this.element.querySelector(".d-editor-input");
+    const selectionStart = textArea.selectionStart;
+    if (selectionStart === 0) {
+      return true;
+    } else if (textArea.value.charAt(selectionStart - 1) === "\n") {
+      return true;
+    } else {
+      return false;
+    }
+  },
+
   actions: {
     importQuote(toolbarEvent) {
       this.importQuote(toolbarEvent);
diff --git a/app/assets/javascripts/discourse/tests/acceptance/composer-attachment-test.js b/app/assets/javascripts/discourse/tests/acceptance/composer-attachment-test.js
index 51e7b1806b8..1413cd5c16d 100644
--- a/app/assets/javascripts/discourse/tests/acceptance/composer-attachment-test.js
+++ b/app/assets/javascripts/discourse/tests/acceptance/composer-attachment-test.js
@@ -1,4 +1,8 @@
-import { acceptance, queryAll } from "discourse/tests/helpers/qunit-helpers";
+import {
+  acceptance,
+  query,
+  queryAll,
+} from "discourse/tests/helpers/qunit-helpers";
 import { click, fillIn, visit } from "@ember/test-helpers";
 import { test } from "qunit";
 
@@ -28,7 +32,7 @@ async function writeInComposer(assert) {
   await fillIn(".d-editor-input", "[test|attachment](upload://asdsad.png)");
 }
 
-acceptance("Composer Attachment", function (needs) {
+acceptance("Composer Attachment - Cooking", function (needs) {
   needs.user();
   needs.pretender(pretender);
 
@@ -54,3 +58,198 @@ acceptance("Composer Attachment - Secure Media Enabled", function (needs) {
     );
   });
 });
+
+acceptance("Composer Attachment - Upload Placeholder", function (needs) {
+  needs.user();
+
+  test("should insert a newline before and after an image when pasting into an empty composer", async function (assert) {
+    await visit("/");
+    await click("#create-topic");
+    const image = createImage("avatar.png", "/images/avatar.png?1", 200, 300);
+
+    await queryAll(".wmd-controls").trigger("fileuploadsend", image);
+    assert.equal(
+      queryAll(".d-editor-input").val(),
+      "[Uploading: avatar.png...]()\n"
+    );
+
+    await queryAll(".wmd-controls").trigger("fileuploaddone", image);
+    assert.equal(
+      queryAll(".d-editor-input").val(),
+      "![avatar|200x300](/images/avatar.png?1)\n"
+    );
+  });
+
+  test("should insert a newline after an image when pasting into a blank line", async function (assert) {
+    await visit("/");
+    await click("#create-topic");
+    await fillIn(".d-editor-input", "The image:\n");
+
+    const image = createImage("avatar.png", "/images/avatar.png?1", 200, 300);
+    await queryAll(".wmd-controls").trigger("fileuploadsend", image);
+
+    assert.equal(
+      queryAll(".d-editor-input").val(),
+      "The image:\n[Uploading: avatar.png...]()\n"
+    );
+
+    await queryAll(".wmd-controls").trigger("fileuploaddone", image);
+    assert.equal(
+      queryAll(".d-editor-input").val(),
+      "The image:\n![avatar|200x300](/images/avatar.png?1)\n"
+    );
+  });
+
+  test("should insert a newline before and after an image when pasting into a non blank line", async function (assert) {
+    await visit("/");
+    await click("#create-topic");
+    await fillIn(".d-editor-input", "The image:");
+
+    const image = createImage("avatar.png", "/images/avatar.png?1", 200, 300);
+    await queryAll(".wmd-controls").trigger("fileuploadsend", image);
+
+    assert.equal(
+      queryAll(".d-editor-input").val(),
+      "The image:\n[Uploading: avatar.png...]()\n"
+    );
+
+    await queryAll(".wmd-controls").trigger("fileuploaddone", image);
+    assert.equal(
+      queryAll(".d-editor-input").val(),
+      "The image:\n![avatar|200x300](/images/avatar.png?1)\n"
+    );
+  });
+
+  test("should insert a newline before and after an image when pasting with cursor in the middle of the line", async function (assert) {
+    await visit("/");
+    await click("#create-topic");
+    await fillIn(".d-editor-input", "The image Text after the image.");
+    const textArea = query(".d-editor-input");
+    textArea.selectionStart = 10;
+    textArea.selectionEnd = 10;
+
+    const image = createImage("avatar.png", "/images/avatar.png?1", 200, 300);
+    await queryAll(".wmd-controls").trigger("fileuploadsend", image);
+
+    assert.equal(
+      queryAll(".d-editor-input").val(),
+      "The image \n[Uploading: avatar.png...]()\nText after the image."
+    );
+
+    await queryAll(".wmd-controls").trigger("fileuploaddone", image);
+    assert.equal(
+      queryAll(".d-editor-input").val(),
+      "The image \n![avatar|200x300](/images/avatar.png?1)\nText after the image."
+    );
+  });
+
+  test("should insert a newline before and after an image when pasting with text selected", async function (assert) {
+    await visit("/");
+    await click("#create-topic");
+    const image = createImage("avatar.png", "/images/avatar.png?1", 200, 300);
+    await fillIn(
+      ".d-editor-input",
+      "The image [paste here] Text after the image."
+    );
+    const textArea = query(".d-editor-input");
+    textArea.selectionStart = 10;
+    textArea.selectionEnd = 23;
+
+    await queryAll(".wmd-controls").trigger("fileuploadsend", image);
+    assert.equal(
+      queryAll(".d-editor-input").val(),
+      "The image \n[Uploading: avatar.png...]()\n Text after the image."
+    );
+
+    await queryAll(".wmd-controls").trigger("fileuploaddone", image);
+    assert.equal(
+      queryAll(".d-editor-input").val(),
+      "The image \n![avatar|200x300](/images/avatar.png?1)\n Text after the image."
+    );
+  });
+
+  test("pasting several images", async function (assert) {
+    await visit("/");
+    await click("#create-topic");
+
+    const image1 = createImage("test.png", "/images/avatar.png?1", 200, 300);
+    const image2 = createImage("test.png", "/images/avatar.png?2", 100, 200);
+    const image3 = createImage("image.png", "/images/avatar.png?3", 300, 400);
+    const image4 = createImage("image.png", "/images/avatar.png?4", 300, 400);
+
+    await queryAll(".wmd-controls").trigger("fileuploadsend", image1);
+    assert.equal(
+      queryAll(".d-editor-input").val(),
+      "[Uploading: test.png...]()\n"
+    );
+
+    await queryAll(".wmd-controls").trigger("fileuploadsend", image2);
+    assert.equal(
+      queryAll(".d-editor-input").val(),
+      "[Uploading: test.png...]()\n[Uploading: test.png(1)...]()\n"
+    );
+
+    await queryAll(".wmd-controls").trigger("fileuploadsend", image4);
+    assert.equal(
+      queryAll(".d-editor-input").val(),
+      "[Uploading: test.png...]()\n[Uploading: test.png(1)...]()\n[Uploading: image.png...]()\n"
+    );
+
+    await queryAll(".wmd-controls").trigger("fileuploadsend", image3);
+    assert.equal(
+      queryAll(".d-editor-input").val(),
+      "[Uploading: test.png...]()\n[Uploading: test.png(1)...]()\n[Uploading: image.png...]()\n[Uploading: image.png(1)...]()\n"
+    );
+
+    await queryAll(".wmd-controls").trigger("fileuploaddone", image2);
+    assert.equal(
+      queryAll(".d-editor-input").val(),
+      "[Uploading: test.png...]()\n![test|100x200](/images/avatar.png?2)\n[Uploading: image.png...]()\n[Uploading: image.png(1)...]()\n"
+    );
+
+    await queryAll(".wmd-controls").trigger("fileuploaddone", image3);
+    assert.equal(
+      queryAll(".d-editor-input").val(),
+      "[Uploading: test.png...]()\n![test|100x200](/images/avatar.png?2)\n[Uploading: image.png...]()\n![image|300x400](/images/avatar.png?3)\n"
+    );
+
+    await queryAll(".wmd-controls").trigger("fileuploaddone", image1);
+    assert.equal(
+      queryAll(".d-editor-input").val(),
+      "![test|200x300](/images/avatar.png?1)\n![test|100x200](/images/avatar.png?2)\n[Uploading: image.png...]()\n![image|300x400](/images/avatar.png?3)\n"
+    );
+  });
+
+  test("should accept files with unescaped characters", async function (assert) {
+    await visit("/");
+    await click("#create-topic");
+
+    const image = createImage("ima++ge.png", "/images/avatar.png?4", 300, 400);
+
+    await queryAll(".wmd-controls").trigger("fileuploadsend", image);
+    assert.equal(
+      queryAll(".d-editor-input").val(),
+      "[Uploading: ima++ge.png...]()\n"
+    );
+
+    await queryAll(".wmd-controls").trigger("fileuploaddone", image);
+    assert.equal(
+      queryAll(".d-editor-input").val(),
+      "![ima++ge|300x400](/images/avatar.png?4)\n"
+    );
+  });
+
+  function createImage(name, url, width, height) {
+    const file = new Blob([""], { type: "image/png" });
+    file.name = name;
+    return {
+      files: [file],
+      result: {
+        original_filename: name,
+        thumbnail_width: width,
+        thumbnail_height: height,
+        url: url,
+      },
+    };
+  }
+});
diff --git a/app/assets/javascripts/discourse/tests/acceptance/composer-test.js b/app/assets/javascripts/discourse/tests/acceptance/composer-test.js
index 99236da5eb9..f60b38f3d67 100644
--- a/app/assets/javascripts/discourse/tests/acceptance/composer-test.js
+++ b/app/assets/javascripts/discourse/tests/acceptance/composer-test.js
@@ -118,102 +118,6 @@ acceptance("Composer", function (needs) {
     assert.ok(!exists(".bootbox.modal"), "the confirmation can be cancelled");
   });
 
-  test("Composer upload placeholder", async function (assert) {
-    await visit("/");
-    await click("#create-topic");
-
-    const file1 = new Blob([""], { type: "image/png" });
-    file1.name = "test.png";
-    const data1 = {
-      files: [file1],
-      result: {
-        original_filename: "test.png",
-        thumbnail_width: 200,
-        thumbnail_height: 300,
-        url: "/images/avatar.png?1",
-      },
-    };
-
-    const file2 = new Blob([""], { type: "image/png" });
-    file2.name = "test.png";
-    const data2 = {
-      files: [file2],
-      result: {
-        original_filename: "test.png",
-        thumbnail_width: 100,
-        thumbnail_height: 200,
-        url: "/images/avatar.png?2",
-      },
-    };
-
-    const file3 = new Blob([""], { type: "image/png" });
-    file3.name = "image.png";
-    const data3 = {
-      files: [file3],
-      result: {
-        original_filename: "image.png",
-        thumbnail_width: 300,
-        thumbnail_height: 400,
-        url: "/images/avatar.png?3",
-      },
-    };
-
-    const file4 = new Blob([""], { type: "image/png" });
-    file4.name = "ima++ge.png";
-    const data4 = {
-      files: [file4],
-      result: {
-        original_filename: "ima++ge.png",
-        thumbnail_width: 300,
-        thumbnail_height: 400,
-        url: "/images/avatar.png?3",
-      },
-    };
-
-    await queryAll(".wmd-controls").trigger("fileuploadsend", data1);
-    assert.equal(
-      queryAll(".d-editor-input").val(),
-      "[Uploading: test.png...]() "
-    );
-
-    await queryAll(".wmd-controls").trigger("fileuploadsend", data2);
-    assert.equal(
-      queryAll(".d-editor-input").val(),
-      "[Uploading: test.png...]() [Uploading: test.png(1)...]() "
-    );
-
-    await queryAll(".wmd-controls").trigger("fileuploadsend", data4);
-    assert.equal(
-      queryAll(".d-editor-input").val(),
-      "[Uploading: test.png...]() [Uploading: test.png(1)...]() [Uploading: ima++ge.png...]() ",
-      "should accept files with unescaped characters"
-    );
-
-    await queryAll(".wmd-controls").trigger("fileuploadsend", data3);
-    assert.equal(
-      queryAll(".d-editor-input").val(),
-      "[Uploading: test.png...]() [Uploading: test.png(1)...]() [Uploading: ima++ge.png...]() [Uploading: image.png...]() "
-    );
-
-    await queryAll(".wmd-controls").trigger("fileuploaddone", data2);
-    assert.equal(
-      queryAll(".d-editor-input").val(),
-      "[Uploading: test.png...]() ![test|100x200](/images/avatar.png?2) [Uploading: ima++ge.png...]() [Uploading: image.png...]() "
-    );
-
-    await queryAll(".wmd-controls").trigger("fileuploaddone", data3);
-    assert.equal(
-      queryAll(".d-editor-input").val(),
-      "[Uploading: test.png...]() ![test|100x200](/images/avatar.png?2) [Uploading: ima++ge.png...]() ![image|300x400](/images/avatar.png?3) "
-    );
-
-    await queryAll(".wmd-controls").trigger("fileuploaddone", data1);
-    assert.equal(
-      queryAll(".d-editor-input").val(),
-      "![test|200x300](/images/avatar.png?1) ![test|100x200](/images/avatar.png?2) [Uploading: ima++ge.png...]() ![image|300x400](/images/avatar.png?3) "
-    );
-  });
-
   test("Create a topic with server side errors", async function (assert) {
     await visit("/");
     await click("#create-topic");