diff --git a/markdown-mode.el b/markdown-mode.el
index 1dbd185b..8884e0e2 100644
--- a/markdown-mode.el
+++ b/markdown-mode.el
@@ -1096,12 +1096,23 @@ update the buffer containing the preview and return the buffer."
   :type 'function)
 
 (defcustom markdown-live-preview-delete-export 'delete-on-destroy
-  "Delete exported html file when using `markdown-live-preview-export' on every
+  "Delete exported html file when using `markdown-live-preview-mode' on every
 export by setting to 'delete-on-export, when quitting
 `markdown-live-preview-mode' by setting to 'delete-on-destroy, or not at all
 when nil."
   :group 'markdown
-  :type 'symbol)
+  :type '(symbol :validate markdown-live-preview-validate-delete-export))
+
+(defcustom markdown-live-preview-idle-delay 1
+  "Delay to wait idle before refreshing `markdown-live-preview-mode' output."
+  :group 'markdown
+  :type 'integer)
+
+(defcustom markdown-live-preview-do-sync nil
+  "Whether to do live preview exports synchronously on save, or asynchronously
+with an idle timer."
+  :group 'markdown
+  :type 'boolean)
 
 (defcustom markdown-list-indent-width 4
   "Depth of indentation for markdown lists. Used in `markdown-demote-list-item'
@@ -4610,7 +4621,7 @@ See also `markdown-mode-map'.")
     ["Export" markdown-export]
     ["Export & View" markdown-export-and-preview]
     ["Open" markdown-open]
-    ["Live Export" markdown-live-preview-mode
+    ["Live Preview" markdown-live-preview-mode
      :style toggle :selected markdown-live-preview-mode]
     ["Kill ring save" markdown-kill-ring-save]
     "---"
@@ -5844,145 +5855,295 @@ current filename, but with the extension removed and replaced with .html."
   (interactive)
   (browse-url-of-file (markdown-export)))
 
-(defvar markdown-live-preview-buffer nil
-  "Buffer used to preview markdown output in `markdown-live-preview-export'.")
-(make-variable-buffer-local 'markdown-live-preview-buffer)
+(defun markdown-open ()
+  "Open file for the current buffer with `markdown-open-command'."
+  (interactive)
+  (if (not markdown-open-command)
+      (error "Variable `markdown-open-command' must be set")
+    (if (not buffer-file-name)
+        (error "Must be visiting a file")
+      (call-process markdown-open-command
+                    nil nil nil buffer-file-name))))
+
+(defun markdown-kill-ring-save ()
+  "Run Markdown on file and store output in the kill ring."
+  (interactive)
+  (save-window-excursion
+    (markdown)
+    (with-current-buffer markdown-output-buffer-name
+      (kill-ring-save (point-min) (point-max)))))
+
+
+;;; Live Preview ===============================================================
+
+(defun markdown-live-preview-validate-delete-export (widget)
+  (let ((sym (widget-value widget)))
+    (unless (memq sym '(delete-on-export delete-on-destroy nil))
+      (widget-put widget :error (format "Invalid value: '%s'"
+                                        (symbol-name sym)))
+      widget)))
+
+(defvar markdown-live-preview-view-buffer nil
+  "Buffer used to preview markdown output in
+`markdown-live-preview-sync-export' and `markdown-live-preview-async-export'.")
+(make-variable-buffer-local 'markdown-live-preview-view-buffer)
 
 (defvar markdown-live-preview-source-buffer nil
   "Buffer with markdown source generating the source of the current
-buffer. Inverse of `markdown-live-preview-buffer'.")
+buffer. Inverse of `markdown-live-preview-view-buffer'.")
 (make-variable-buffer-local 'markdown-live-preview-source-buffer)
 
-(defvar markdown-live-preview-currently-exporting nil)
+(defvar markdown-live-preview-currently-exporting-process nil
+  "Asynchronous process currently running, generated by
+`markdown-live-preview-async-export'.")
+(make-variable-buffer-local 'markdown-live-preview-currently-exporting-process)
 
-(defun markdown-live-preview-get-filename ()
-  "Standardize the filename exported by `markdown-live-preview-export'."
-  (markdown-export-file-name ".html"))
+(defvar markdown-live-preview-idle-timer nil
+  "Idle timer updating live previews using
+`markdown-live-preview-async-export'.")
+(make-variable-buffer-local 'markdown-live-preview-idle-timer)
+
+(defvar markdown-live-preview-current-buffer-sync-async nil
+  "Status of sync or async live preview in current buffer.")
+(make-variable-buffer-local 'markdown-live-preview-current-buffer-sync-async)
+
+(defun markdown-live-preview-has-eww-p ()
+  (and (require 'eww nil t)
+       (fboundp 'libxml-parse-html-region)))
 
 (defun markdown-live-preview-window-eww (file)
-  "A `markdown-live-preview-window-function' for previewing with eww."
-  (if (require 'eww nil t)
+  "A `markdown-live-preview-window-function' for previewing with `eww'."
+  (if (markdown-live-preview-has-eww-p)
       (progn
         (eww-open-file file)
         (get-buffer "*eww*"))
     (error "eww is not present or not loaded on this version of emacs")))
 
+(defun markdown-live-preview-link-source-view-buffers (src-buf view-buf)
+  (with-current-buffer src-buf
+    (setq markdown-live-preview-view-buffer view-buf))
+  (with-current-buffer view-buf
+    (setq markdown-live-preview-source-buffer src-buf)))
+
+(defun markdown-live-preview-get-filename ()
+  "Standardize the filename exported by `markdown-live-preview-async-export'."
+  (markdown-export-file-name ".html"))
+
+(defun markdown-live-preview-delete-export ()
+  (let ((outfile-name (markdown-live-preview-get-filename)))
+    (when (and outfile-name (file-exists-p outfile-name))
+      (delete-file outfile-name))))
+
 (defun markdown-live-preview-window-serialize (buf)
   "Get window point and scroll data for all windows displaying BUF if BUF is
-non-nil."
-  (when buf
+alive."
+  (when (buffer-live-p buf)
     (mapcar (lambda (win) (list win (window-point win) (window-start win)))
             (get-buffer-window-list buf))))
 
-(defun markdown-live-preview-window-deserialize (window-posns)
+(defun markdown-live-preview-window-deserialize (window-posns view-buf)
   "Apply window point and scroll data from WINDOW-POSNS, given by
-`markdown-live-preview-window-serialize'."
+`markdown-live-preview-window-serialize', displaying VIEW-BUF."
   (cl-destructuring-bind (win pt start) window-posns
     (when (window-live-p win)
-      (set-window-buffer win markdown-live-preview-buffer)
+      (set-window-buffer win view-buf)
       (set-window-point win pt)
       (set-window-start win start))))
 
-(defun markdown-live-preview-export ()
+(defun markdown-live-preview-async-cleanup-export (buf msg)
+  (with-current-buffer buf
+    (when (eq markdown-live-preview-delete-export 'delete-on-export)
+      (markdown-live-preview-delete-export))
+    (setq markdown-live-preview-currently-exporting-process nil))
+  (message msg))
+
+(defun markdown-live-preview-get-displaying-window (window-data-list)
+  (or (caar window-data-list)
+      ;; if not displaying buf in any window, then make a new window and display
+      ;; it there
+      (markdown-display-buffer-other-window (window-buffer))))
+
+(defun markdown-live-preview-create-display (out-file &optional src-buf)
+  (let* ((src-buf (or src-buf (current-buffer)))
+         (had-view-buffer
+          (with-current-buffer src-buf markdown-live-preview-view-buffer))
+         (window-data-list
+          (with-current-buffer src-buf
+            (markdown-live-preview-window-serialize
+             markdown-live-preview-view-buffer)))
+         (display-win
+          (markdown-live-preview-get-displaying-window window-data-list))
+         (view-buf
+          (save-window-excursion
+            (with-selected-window display-win
+              (funcall markdown-live-preview-window-function out-file)))))
+    (markdown-live-preview-link-source-view-buffers src-buf view-buf)
+    (with-current-buffer view-buf
+      (add-hook 'kill-buffer-hook #'markdown-live-preview-teardown-view t t))
+    (with-current-buffer src-buf
+      (if had-view-buffer
+          (if window-data-list
+              (cl-loop for window-data in window-data-list
+                       do (markdown-live-preview-window-deserialize
+                           window-data view-buf))
+            (delete-window display-win))
+        (set-window-buffer display-win view-buf)))))
+
+(defun markdown-live-preview-async-create-view-display (src-buf out-file msg)
+  (unwind-protect
+      (markdown-live-preview-create-display out-file src-buf)
+    (markdown-live-preview-async-cleanup-export src-buf msg)))
+
+(defun markdown-live-preview-do-sentinel (src-buf out-file proc)
+  (unless (or (process-live-p proc)
+              (not (buffer-live-p src-buf)))
+    (let ((cur-msg (current-message))
+          (proc-buf (process-buffer proc)))
+      (kill-buffer proc-buf)
+      (markdown-live-preview-async-create-view-display
+       src-buf out-file cur-msg))))
+
+(defun markdown-live-preview-make-async-sentinel (src-buf out-file)
+  (lambda (proc _) (markdown-live-preview-do-sentinel src-buf out-file proc)))
+
+(defconst markdown-live-preview-proc-name "*markdown-live-preview*")
+(defconst markdown-live-preview-buf-name "*markdown-live-preview-output*")
+
+(defun markdown-live-preview-async-export ()
+  (interactive)
+  (unless markdown-live-preview-currently-exporting-process
+    (let* ((cur-buf (current-buffer))
+           (mode major-mode)
+           (out-file (markdown-live-preview-get-filename))
+           (sentinel (markdown-live-preview-make-async-sentinel
+                      cur-buf out-file))
+           (proc
+            (start-process-shell-command
+             markdown-live-preview-proc-name markdown-live-preview-buf-name
+             ;; using redirection means emacs doesn't have to process output,
+             ;; which interrupts the user less
+             (concat
+              markdown-command " "
+              (and markdown-command-needs-filename
+                   (if (buffer-file-name)
+                       (shell-quote-argument (buffer-file-name))
+                     (user-error "Must be visiting a file")))
+              " > " (shell-quote-argument out-file)))))
+      (setq markdown-live-preview-currently-exporting-process proc)
+      (set-process-sentinel proc sentinel)
+      (with-temp-buffer
+        (let ((tmp-buf (current-buffer)))
+          (with-current-buffer cur-buf
+            (copy-to-buffer tmp-buf (point-min) (point-max))))
+        (funcall mode)
+        (run-hooks 'markdown-before-export-hook)
+        (process-send-region proc (point-min) (point-max))
+        (process-send-eof proc)
+        (run-hooks 'markdown-after-export-hook)
+        proc))))
+
+(defvar markdown-live-preview-currently-exporting nil)
+
+(defun markdown-live-preview-sync-export ()
   "Export to XHTML using `markdown-export' and browse the resulting file within
 Emacs using `markdown-live-preview-window-function' Return the buffer displaying
 the rendered output."
   (interactive)
   (let* ((markdown-live-preview-currently-exporting t)
-         (cur-buf (current-buffer))
-         (export-file (markdown-export (markdown-live-preview-get-filename)))
-         ;; get positions in all windows currently displaying output buffer
-         (window-data
-          (markdown-live-preview-window-serialize
-           markdown-live-preview-buffer)))
-    (save-window-excursion
-      (let ((output-buffer
-             (funcall markdown-live-preview-window-function export-file)))
-        (with-current-buffer output-buffer
-          (setq markdown-live-preview-source-buffer cur-buf)
-          (add-hook 'kill-buffer-hook
-                    #'markdown-live-preview-remove-on-kill t t))
-        (with-current-buffer cur-buf
-          (setq markdown-live-preview-buffer output-buffer))))
-    (with-current-buffer cur-buf
-      ;; reset all windows displaying output buffer to where they were,
-      ;; now with the new output
-      (mapc #'markdown-live-preview-window-deserialize window-data)
-      ;; delete html editing buffer
-      (let ((buf (get-file-buffer export-file))) (when buf (kill-buffer buf)))
-      (when (and export-file (file-exists-p export-file)
-                 (eq markdown-live-preview-delete-export
-                     'delete-on-export))
-        (delete-file export-file))
-      markdown-live-preview-buffer)))
-
-(defun markdown-live-preview-remove ()
-  (when (buffer-live-p markdown-live-preview-buffer)
-    (kill-buffer markdown-live-preview-buffer))
-  (setq markdown-live-preview-buffer nil)
+         (out-file (markdown-export (markdown-live-preview-get-filename))))
+    (markdown-live-preview-create-display out-file)
+    ;; delete html editing buffer
+    (let ((buf (get-file-buffer out-file)))
+      (when buf (kill-buffer buf)))
+    (when (and out-file (file-exists-p out-file)
+               (eq markdown-live-preview-delete-export
+                   'delete-on-export))
+      (delete-file out-file))
+    markdown-live-preview-view-buffer))
+
+(defun markdown-live-preview-update (buf)
+  (lambda () (with-current-buffer buf (markdown-live-preview-async-export))))
+
+(defun markdown-live-preview-setup ()
+  (setq markdown-live-preview-current-buffer-sync-async
+        markdown-live-preview-do-sync)
+  (add-hook 'kill-buffer-hook #'markdown-live-preview-teardown-source t t)
+  (if markdown-live-preview-current-buffer-sync-async
+      (progn
+        (add-hook
+         'after-save-hook #'markdown-live-preview-do-sync-preview t t)
+        (markdown-live-preview-sync-export))
+    (setq markdown-live-preview-idle-timer
+          (run-with-idle-timer
+           markdown-live-preview-idle-delay t
+           (markdown-live-preview-update (current-buffer))))
+    (markdown-live-preview-async-export)))
+
+(defun markdown-live-preview-teardown-view ()
+  (with-current-buffer markdown-live-preview-source-buffer
+    (setq markdown-live-preview-view-buffer nil))
+  (setq markdown-live-preview-source-buffer nil))
+
+(defun markdown-live-preview-teardown-async ()
+  (when (buffer-live-p markdown-live-preview-view-buffer)
+    ;; calls `kill-buffer-hook' within `markdown-live-preview-view-buffer'
+    (kill-buffer markdown-live-preview-view-buffer))
+  (when (and markdown-live-preview-currently-exporting-process
+             (process-live-p markdown-live-preview-currently-exporting-process))
+    (set-process-sentinel markdown-live-preview-currently-exporting-process nil)
+    (delete-process markdown-live-preview-currently-exporting-process))
+  (setq markdown-live-preview-view-buffer nil
+        markdown-live-preview-currently-exporting-process nil)
+  ;; if set to 'delete-on-export, the output has already been deleted
+  (when (eq markdown-live-preview-delete-export 'delete-on-destroy)
+    (markdown-live-preview-delete-export))
+  (when markdown-live-preview-idle-timer
+    (cancel-timer markdown-live-preview-idle-timer)
+    (setq markdown-live-preview-idle-timer nil))
+  (remove-hook 'kill-buffer-hook #'markdown-live-preview-teardown-source t))
+
+(defun markdown-live-preview-teardown-sync ()
+  (when (buffer-live-p markdown-live-preview-view-buffer)
+    (kill-buffer markdown-live-preview-view-buffer))
+  (setq markdown-live-preview-view-buffer nil)
+  (remove-hook 'after-save-hook #'markdown-live-preview-do-sync-preview t)
   ;; if set to 'delete-on-export, the output has already been deleted
   (when (eq markdown-live-preview-delete-export 'delete-on-destroy)
     (let ((outfile-name (markdown-live-preview-get-filename)))
       (when (file-exists-p outfile-name)
         (delete-file outfile-name)))))
 
+(defun markdown-live-preview-teardown-source ()
+  (if markdown-live-preview-current-buffer-sync-async
+      (markdown-live-preview-teardown-sync)
+    (markdown-live-preview-teardown-async)))
+
 (defun markdown-display-buffer-other-window (buf)
-  (let ((cur-buf (current-buffer)))
-    (switch-to-buffer-other-window buf)
-    (set-buffer cur-buf)))
-
-(defun markdown-live-preview-if-markdown ()
-  (when (and (derived-mode-p 'markdown-mode)
-             markdown-live-preview-mode)
-    (unless markdown-live-preview-currently-exporting
-      (if (buffer-live-p markdown-live-preview-buffer)
-          (markdown-live-preview-export)
-        (markdown-display-buffer-other-window
-         (markdown-live-preview-export))))))
-
-(defun markdown-live-preview-remove-on-kill ()
-  (cond ((and (derived-mode-p 'markdown-mode)
-              markdown-live-preview-mode)
-         (markdown-live-preview-remove))
-        (markdown-live-preview-source-buffer
-         (with-current-buffer markdown-live-preview-source-buffer
-           (setq markdown-live-preview-buffer nil))
-         (setq markdown-live-preview-source-buffer nil))))
+  (let ((pop-up-windows t))
+    (select-window (display-buffer buf t))))
 
 (defun markdown-live-preview-switch-to-output ()
   (interactive)
   "Turn on `markdown-live-preview-mode' if not already on, and switch to its
 output buffer in another window."
   (if markdown-live-preview-mode
-      (markdown-display-buffer-other-window (markdown-live-preview-export)))
+      (markdown-display-buffer-other-window markdown-live-preview-view-buffer))
     (markdown-live-preview-mode))
 
 (defun markdown-live-preview-re-export ()
   (interactive)
   "If the current buffer is a buffer displaying the exported version of a
-`markdown-live-preview-mode' buffer, call `markdown-live-preview-export' and
-update this buffer's contents."
+`markdown-live-preview-mode' buffer, call `markdown-live-preview-async-export'
+or `markdown-live-preview-sync-export' and update this buffer's contents."
   (when markdown-live-preview-source-buffer
     (with-current-buffer markdown-live-preview-source-buffer
-      (markdown-live-preview-export))))
-
-(defun markdown-open ()
-  "Open file for the current buffer with `markdown-open-command'."
-  (interactive)
-  (if (not markdown-open-command)
-      (error "Variable `markdown-open-command' must be set")
-    (if (not buffer-file-name)
-        (error "Must be visiting a file")
-      (call-process markdown-open-command
-                    nil nil nil buffer-file-name))))
+      (if markdown-live-preview-current-buffer-sync-async
+          (markdown-live-preview-sync-export)
+        (markdown-live-preview-async-export)))))
 
-(defun markdown-kill-ring-save ()
-  "Run Markdown on file and store output in the kill ring."
-  (interactive)
-  (save-window-excursion
-    (markdown)
-    (with-current-buffer markdown-output-buffer-name
-      (kill-ring-save (point-min) (point-max)))))
+(defun markdown-live-preview-do-sync-preview ()
+  (unless markdown-live-preview-currently-exporting
+    (markdown-live-preview-sync-export)))
 
 
 ;;; Links =====================================================================
@@ -6508,10 +6669,6 @@ before regenerating font-lock rules for extensions."
   (add-hook 'window-configuration-change-hook
             'markdown-fontify-buffer-wiki-links t t)
 
-  ;; add live preview export hook
-  (add-hook 'after-save-hook #'markdown-live-preview-if-markdown t t)
-  (add-hook 'kill-buffer-hook #'markdown-live-preview-remove-on-kill t t)
-
   ;; do the initial link fontification
   (markdown-fontify-buffer-wiki-links))
 
@@ -6550,13 +6707,14 @@ before regenerating font-lock rules for extensions."
   (markdown-gfm-parse-buffer-for-languages))
 
 
-;;; Live Preview Mode  ============================================
+;;; Live Preview Mode  =========================================================
 (define-minor-mode markdown-live-preview-mode
   "Toggle native previewing on save for a specific markdown file."
+  :group 'markdown-mode
   :lighter " MD-Preview"
   (if markdown-live-preview-mode
-      (markdown-display-buffer-other-window (markdown-live-preview-export))
-    (markdown-live-preview-remove)))
+      (markdown-live-preview-setup)
+    (markdown-live-preview-teardown-source)))
 
 
 (provide 'markdown-mode)
diff --git a/tests/markdown-test.el b/tests/markdown-test.el
index c61127d7..d8352882 100644
--- a/tests/markdown-test.el
+++ b/tests/markdown-test.el
@@ -3739,7 +3739,7 @@ Detail: https://github.com/jrblevin/markdown-mode/issues/79"
 
 (defmacro markdown-temp-eww (&rest body)
   `(progn
-     ,@(if (featurep 'eww) body
+     ,@(if (markdown-live-preview-has-eww-p) body
          `((ad-enable-advice #'markdown-live-preview-window-eww
                              'around 'markdown-create-fake-eww)
            (ad-activate #'markdown-live-preview-window-eww)
@@ -3748,45 +3748,163 @@ Detail: https://github.com/jrblevin/markdown-mode/issues/79"
                               'around 'markdown-create-fake-eww)
            (ad-activate #'markdown-live-preview-window-eww)))))
 
-(ert-deftest test-markdown-ext/live-preview-exports ()
+(defun markdown-test/live-preview-wait ()
+  (unless markdown-live-preview-current-buffer-sync-async
+    (sit-for (* markdown-live-preview-idle-delay 10))
+    (accept-process-output
+     markdown-live-preview-currently-exporting-process nil nil t)))
+
+(defun markdown-test/live-preview-exports ()
   (markdown-test-temp-file "inline.text"
-    (unless (require 'eww nil t)
-      (should-error (markdown-live-preview-mode)))
-    (markdown-temp-eww
-     (markdown-live-preview-mode)
-     (should (buffer-live-p markdown-live-preview-buffer))
-     (should (eq (current-buffer)
-                 (with-current-buffer markdown-live-preview-buffer
-                   markdown-live-preview-source-buffer)))
-     (kill-buffer markdown-live-preview-buffer)
-     (should (null markdown-live-preview-buffer))
-     (set-buffer-modified-p t)
-     (save-buffer)                      ; should create new export
-     (should (buffer-live-p markdown-live-preview-buffer)))))
-
-(ert-deftest test-markdown-ext/live-preview-delete-exports ()
-  (markdown-temp-eww
-   (let ((markdown-live-preview-delete-export 'delete-on-destroy)
-         file-output)
-     (markdown-test-temp-file "inline.text"
-       (markdown-live-preview-mode)
-       (setq file-output (markdown-export-file-name)))
-     (should-not (file-exists-p file-output)))
-   (let ((markdown-live-preview-delete-export 'delete-on-export)
-         file-output)
-     (markdown-test-temp-file "inline.text"
+    (let ((markdown-live-preview-idle-delay .01))
+      (markdown-temp-eww
        (markdown-live-preview-mode)
-       (setq file-output (markdown-export-file-name))
-       (should-not (file-exists-p file-output))))
-   (let ((markdown-live-preview-delete-export nil)
-         file-output)
+       (markdown-test/live-preview-wait)
+       (should (buffer-live-p markdown-live-preview-view-buffer))
+       (should (eq (current-buffer)
+                   (with-current-buffer markdown-live-preview-view-buffer
+                     markdown-live-preview-source-buffer)))
+       (kill-buffer markdown-live-preview-view-buffer)
+       (should (null markdown-live-preview-view-buffer))
+       (if markdown-live-preview-current-buffer-sync-async
+           (markdown-live-preview-sync-export)
+         (markdown-live-preview-async-export)
+         (markdown-test/live-preview-wait))
+       (should (buffer-live-p markdown-live-preview-view-buffer))))))
+
+(ert-deftest test-markdown-ext/live-preview-exports-sync ()
+  (let ((markdown-live-preview-do-sync t))
+    (unless (markdown-live-preview-has-eww-p)
+      (should-error (markdown-live-preview-sync-export)))
+    (markdown-test/live-preview-exports)))
+
+(ert-deftest test-markdown-ext/live-preview-exports-async ()
+  (markdown-test/live-preview-exports))
+
+(defun markdown-test/live-preview-delete-exports ()
+  (let ((markdown-live-preview-idle-delay .01)
+        file-output)
+    (markdown-temp-eww
+     (let ((markdown-live-preview-delete-export 'delete-on-destroy))
+       (markdown-test-temp-file "inline.text"
+         (markdown-live-preview-mode)
+         (markdown-test/live-preview-wait)
+         (setq file-output (markdown-export-file-name)))
+       (should-not (file-exists-p file-output)))
+     (let ((markdown-live-preview-delete-export 'delete-on-export))
+       (markdown-test-temp-file "inline.text"
+         (markdown-live-preview-mode)
+         (markdown-test/live-preview-wait)
+         (setq file-output (markdown-export-file-name))
+         (should-not (file-exists-p file-output))))
      (unwind-protect
          (markdown-test-temp-file "inline.text"
            (markdown-live-preview-mode)
+           (markdown-test/live-preview-wait)
            (setq file-output (markdown-export-file-name))
+           (setq markdown-live-preview-delete-export nil)
            (should (file-exists-p file-output)))
        (delete-file file-output)))))
 
+(ert-deftest test-markdown-ext/live-preview-delete-exports-sync ()
+  (let ((markdown-live-preview-do-sync t))
+    (markdown-test/live-preview-delete-exports)))
+
+(ert-deftest test-markdown-ext/live-preview-delete-exports-async ()
+  (markdown-test/live-preview-delete-exports))
+
+(defvar markdown-test-eww-window nil)
+(defvar markdown-test-hit-advice nil)
+
+(defadvice eww-open-file (before markdown-test-set-window-width disable)
+  (setq markdown-test-hit-advice t)
+  (should (eq (selected-window) markdown-test-eww-window)))
+
+(defadvice get-buffer-create (before markdown-set-window-width-mock disable)
+  (when (let ((buf (ad-get-arg 0))) (and (stringp buf) (string= buf "*eww*")))
+    (setq markdown-test-hit-advice t)
+    (should (eq (selected-window) markdown-test-eww-window))))
+
+(defmacro markdown-eww-open-file-advice (&rest body)
+  (if (markdown-live-preview-has-eww-p)
+      `(progn
+         (ad-enable-advice #'eww-open-file 'before
+                           'markdown-test-set-window-width)
+         (ad-activate #'eww-open-file)
+         ,@body
+         (ad-disable-advice #'eww-open-file 'before
+                            'markdown-test-set-window-width)
+         (ad-activate #'eww-open-file))
+    `(progn
+       (ad-enable-advice #'get-buffer-create 'before
+                         'markdown-set-window-width-mock)
+       (ad-activate #'get-buffer-create)
+       ,@body
+       (ad-disable-advice #'get-buffer-create 'before
+                          'markdown-set-window-width-mock)
+       (ad-activate #'get-buffer-create))))
+
+(defadvice markdown-live-preview-get-displaying-window
+    (after markdown-test-get-test-window disable)
+  (setq markdown-test-eww-window ad-return-value))
+
+(defmacro markdown-initial-window-create-advice (&rest body)
+  `(progn
+     (ad-enable-advice #'markdown-live-preview-get-displaying-window
+                       'after 'markdown-test-get-test-window)
+     (ad-activate #'markdown-live-preview-get-displaying-window)
+     ,@body
+     (ad-disable-advice #'markdown-live-preview-get-displaying-window
+                        'after 'markdown-test-get-test-window)
+     (ad-activate #'markdown-live-preview-get-displaying-window)))
+
+(defun markdown-test/test-window-usage-live-preview ()
+  (setq markdown-test-hit-advice nil)
+  (save-window-excursion
+    (markdown-test-temp-file "inline.text"
+      (let ((markdown-live-preview-idle-delay .01)
+            (windows (window-list))
+            (md-buf (current-buffer)))
+        (cl-loop for win in windows
+                 unless (eq win (selected-window))
+                 do (delete-window win))
+        ;; note that advices are used in this test to supply `should' conditions
+        (markdown-temp-eww
+         ;; test that first window created by
+         ;; `markdown-live-preview-get-displaying-window' is the same window
+         ;; that is used to finally render the buffer, on the first try
+         (markdown-eww-open-file-advice
+          (markdown-initial-window-create-advice
+           (if markdown-live-preview-do-sync
+               (markdown-live-preview-sync-export)
+             (markdown-live-preview-async-export)
+             (markdown-test/live-preview-wait))
+           (should (eq t markdown-test-hit-advice))
+           (setq markdown-test-hit-advice nil))
+          (setq markdown-test-eww-window
+                (get-buffer-window (get-buffer "*eww*")))
+          (should markdown-live-preview-view-buffer)
+          (with-selected-window (get-buffer-window md-buf)
+            (if markdown-live-preview-do-sync
+                (markdown-live-preview-sync-export)
+              (markdown-live-preview-async-export)
+              (markdown-test/live-preview-wait))
+            ;; at this point, `eww-render' should have finished, and eww should
+            ;; have redisplayed. the advice checks that, since there was a
+            ;; single window displaying the *eww* buffer, that window was used
+            ;; as the selected window so that eww renders with a width
+            ;; equal to the width of its window
+            (should (eq t markdown-test-hit-advice))))))))
+  (setq markdown-test-hit-advice nil))
+
+(ert-deftest test-markdown-ext/live-preview-window-size-sync ()
+  (let ((markdown-live-preview-do-sync t))
+    (markdown-test/test-window-usage-live-preview)))
+
+(ert-deftest test-markdown-ext/live-preview-window-size-async ()
+  (let ((markdown-live-preview-do-sync nil))
+    (markdown-test/test-window-usage-live-preview)))
+
 (provide 'markdown-test)
 
 ;;; markdown-test.el ends here