File Preview Modal in Lightning Web Component Salesforce ☁️⚡️

 In this blog I will share the code to preview files in a Modal in Lightning Web Component.

There are three component in this example - 

1. PreviewFileThumbnails - The parent component to upload the file.

2. PreviewFileThumbnailCard - Child component to display image preview in a thumbnail gallery view.

3. PreviewFileModal - Child component to display selected thumbnail file in a new modal popup for preview. 


Make sure to change below PDF setting in Salesforce org to see the preview in browser otherwise it will download the file instead of preview : 


Go to Setup --> Security --> File Upload and Download Security

Hit edit and update the .pdf file settings to "Execute in Browser" as shown in the image below : 



You may follow the code below now.

PreviewFileThumbnails.html

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
<template>
  <lightning-file-upload
    name="fileUploader"
    accept={acceptedFormats}
    record-id={recordId}
    onuploadfinished={handleUploadFinished}
    multiple
  >
  </lightning-file-upload>

  <br />
  <div
    class="
      slds-page-header__row
      slds-var-m-top_x-small
      slds-var-m-left_medium
      slds-grid slds-wrap
    "
  >
    <ul class="slds-grid slds-wrap slds-gutters">
      <template if:true={loaded}>
        <template for:each={files} for:item="file">
          <c-preview-file-thumbnail-card
            key={file.Id}
            file={file}
            record-id={recordId}
            thumbnail={file.thumbnailFileCard}
          ></c-preview-file-thumbnail-card>
        </template>
      </template>
    </ul>
  </div>
</template>


PreviewFileThumbnails.js

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
import { LightningElement, wire, api, track } from "lwc";
import { refreshApex } from "@salesforce/apex";
import { ShowToastEvent } from "lightning/platformShowToastEvent";
import getFileVersions from "@salesforce/apex/FileController.getVersionFiles";

export default class PreviewFileThumbnails extends LightningElement {
  loaded = false;
  @track fileList;
  @api recordId;
  @track files = [];
  get acceptedFormats() {
    return [".pdf", ".png", ".jpg", ".jpeg"];
  }

  @wire(getFileVersions, { recordId: "$recordId" })
  fileResponse(value) {
    this.wiredActivities = value;
    const { data, error } = value;
    this.fileList = "";
    this.files = [];
    if (data) {
      this.fileList = data;
      for (let i = 0; i < this.fileList.length; i++) {
        let file = {
          Id: this.fileList[i].Id,
          Title: this.fileList[i].Title,
          Extension: this.fileList[i].FileExtension,
          ContentDocumentId: this.fileList[i].ContentDocumentId,
          ContentDocument: this.fileList[i].ContentDocument,
          CreatedDate: this.fileList[i].CreatedDate,
          thumbnailFileCard:
            "/sfc/servlet.shepherd/version/renditionDownload?rendition=THUMB720BY480&versionId=" +
            this.fileList[i].Id +
            "&operationContext=CHATTER&contentId=" +
            this.fileList[i].ContentDocumentId,
          downloadUrl:
            "/sfc/servlet.shepherd/document/download/" +
            this.fileList[i].ContentDocumentId
        };
        this.files.push(file);
      }
      this.loaded = true;
    } else if (error) {
      this.dispatchEvent(
        new ShowToastEvent({
          title: "Error loading Files",
          message: error.body.message,
          variant: "error"
        })
      );
    }
  }
  handleUploadFinished(event) {
    const uploadedFiles = event.detail.files;
    refreshApex(this.wiredActivities);
    this.dispatchEvent(
      new ShowToastEvent({
        title: "Success!",
        message: uploadedFiles.length + " Files Uploaded Successfully.",
        variant: "success"
      })
    );
  }
}


PreviewFileThumbnailCard.html

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
<template>
  <template if:true={file}>
    <c-preview-file-modal
      url={file.downloadUrl}
      file-extension={file.Extension}
    ></c-preview-file-modal>
    <li
      class="
        slds-col
        slds-var-p-vertical_x-small
        slds-small-size_1-of-2
        slds-medium-size_1-of-4
        slds-large-size_1-of-6
      "
    >
      <div
        class="slds-file slds-file_card slds-has-title"
        style="width: 14rem"
        onclick={filePreview}
      >
        <figure>
          <a class="slds-file__crop slds-file__crop_4-by-3 slds-m-top_x-small">
            <img src={thumbnail} alt={file.title} onclick={filePreview} />
          </a>
          <figcaption class="slds-file__title slds-file__title_card">
            <div class="slds-media slds-media_small slds-media_center">
              <lightning-icon
                icon-name={iconName}
                alternative-text={file.Title}
                title={file.Title}
                size="xx-small"
              >
                <span class="slds-assistive-text"
                  >{file.Title}.{file.Extension}</span
                >
              </lightning-icon>
              <div class="slds-media__body slds-var-p-left_xx-small">
                <span class="slds-file__text slds-truncate" title={file.Title}
                  >{file.Title}.{file.Extension}</span
                >
              </div>
            </div>
          </figcaption>
        </figure>

        <div class="slds-is-absolute" style="top: 3px; right: 5px; z-index: 10">
          <lightning-button-menu
            alternative-text="Show File Menu"
            variant="border-filled"
            icon-size="xx-small"
          >
            <lightning-menu-item
              value="preview"
              label="Preview"
            ></lightning-menu-item>
            <lightning-menu-item
              value="delete"
              label="Delete"
            ></lightning-menu-item>
          </lightning-button-menu>
        </div>
      </div>
    </li>
  </template>
</template>


PreviewFileThumbnailCard.js

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
import { LightningElement, api } from "lwc";

export default class PreviewFileThumbnailCard extends LightningElement {
  @api file;
  @api recordId;
  @api thumbnail;

  get iconName() {
    if (this.file.Extension) {
      if (this.file.Extension === "pdf") {
        return "doctype:pdf";
      }
      if (this.file.Extension === "ppt") {
        return "doctype:ppt";
      }
      if (this.file.Extension === "xls") {
        return "doctype:excel";
      }
      if (this.file.Extension === "csv") {
        return "doctype:csv";
      }
      if (this.file.Extension === "txt") {
        return "doctype:txt";
      }
      if (this.file.Extension === "xml") {
        return "doctype:xml";
      }
      if (this.file.Extension === "doc") {
        return "doctype:word";
      }
      if (this.file.Extension === "zip") {
        return "doctype:zip";
      }
      if (this.file.Extension === "rtf") {
        return "doctype:rtf";
      }
      if (this.file.Extension === "psd") {
        return "doctype:psd";
      }
      if (this.file.Extension === "html") {
        return "doctype:html";
      }
      if (this.file.Extension === "gdoc") {
        return "doctype:gdoc";
      }
    }
    return "doctype:image";
  }

  filePreview() {
    console.log("###Click");
    const showPreview = this.template.querySelector("c-preview-file-modal");
    showPreview.show();
  }
}


PreviewFileModal.html

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
<template>
  <template if:true={showModal}>
    <section
      role="dialog"
      tabindex="-1"
      aria-labelledby="modal-heading-01"
      aria-modal="true"
      aria-describedby="modal-content-id-1"
      class="slds-modal slds-fade-in-open"
    >
      <div class="slds-modal__container">
        <!-- Header Start -->
        <header class="slds-modal__header">
          <lightning-button-icon
            class="slds-modal__close"
            title="Close"
            icon-name="utility:close"
            icon-class="slds-button_icon-inverse"
            onclick={closeModal}
          ></lightning-button-icon>

          <h2 id="id-of-modalheader-h2" class="slds-text-heading_large">
            File Preview
          </h2>
        </header>
        <!-- Header End -->
        <div class="slds-modal__content" id="modal-content-id-1">
          <lightning-layout>
            <lightning-layout-item flexibility="auto">
              <article class="slds-card">
                <div
                  class="slds-card__body slds-card__body_inner"
                  style="margin: 0"
                >
                  <template if:false={showFrame}>
                    <img src={url} />
                  </template>
                  <template if:true={showFrame}>
                    <iframe
                      src={url}
                      style="width: 100%; height: 800px"
                    ></iframe>
                  </template>
                </div>
              </article>
            </lightning-layout-item>
          </lightning-layout>
        </div>
        <footer class="slds-modal__footer slds-grid slds-grid_align-spread">
          <lightning-button
            variant="brand-outline"
            label="Cancel"
            onclick={closeModal}
            title="Cancel"
            class="slds-var-m-left_x-small"
          ></lightning-button>
        </footer>
      </div>
    </section>
    <div class="slds-backdrop slds-backdrop_open"></div>
  </template>
</template>


PreviewFileModal.js

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
import { LightningElement, api } from "lwc";

export default class PreviewFileModal extends LightningElement {
  @api url;
  @api fileExtension;
  showFrame = false;
  showModal = false;
  @api show() {
    console.log("###showFrame : " + this.fileExtension);
    if (this.fileExtension === "pdf") this.showFrame = true;
    else this.showFrame = false;
    this.showModal = true;
  }
  closeModal() {
    this.showModal = false;
  }
}


PreviewFileModal.css

1
2
3
4
.slds-modal__container {
  width: 90% !important;
  max-width: 90% !important;
}


FileController.cls

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
public with sharing class FileController{
@AuraEnabled(cacheable=true)
    public static List<ContentVersion> getVersionFiles(String recordId){
        try {
            return [
		SELECT
		Id,
                Title,
                ContentDocumentId,
                FileType, 
		ContentSize,
		FileExtension,
		VersionNumber,
		CreatedDate,
		VersionData,
                FirstPublishLocationId
		FROM ContentVersion
		WHERE FirstPublishLocationId =:recordId
			ORDER BY CreatedDate DESC
		];
        
        } catch (Exception e) {
            throw new AuraHandledException(e.getMessage());
        }
    }
}


Output


Checkout the complete tutorial below

 If you have any question please leave a comment below.

If you would like to add something to this post please leave a comment below.
Share this blog with your friends if you find it helpful somehow !

Thanks
Happy Coding :)

Post a Comment

10 Comments

  1. Added data-id = contentDocumentId on the div as an attribute

    "div class="slds-file slds-file_card slds-has-title" style="width: 14rem" data-id={file.ContentDocumentId}
    onclick={previewHandler}>
    "
    , but when fetching it in the onclick function as e.target.dataset.id its coming as undefined, even though i can see the value is populated correctly in html.

    console.log(event.target.dataset.id)
    undefined.

    Any idea why ?

    ReplyDelete
    Replies
    1. Got it to work. Used event.currentTarget.dataset.id instead event.target.dataset.id

      Delete
    2. Awesome, thanks for sharing it here !

      Delete
  2. is this approach also applicable for .docx file ?
    because when i'm following this , the .docx files are getting downloaded even though i have changed the security setting

    ReplyDelete
    Replies
    1. Hello there, I believe browsers do not supports word preview as of now. However if you would like to display the preview of your word file you can do it using Google Documents' Viewer via an iframe
      For example in above code you can modify the downloadUrl in PreviewFileThumbnails.js file as shown below to get the result.

      downloadUrl:
      "https://docs.google.com/gview?url=https://speed-dream-9269-dev-ed.scratch.lightning.force.com/sfc/servlet.shepherd/document/download/" +
      this.fileList[i].ContentDocumentId +
      "&embedded=true"

      Delete
    2. Hey Kapil , although i wanted to change docx file in pdf and then preview it using VF page . but there is also a limitation with render as for docx file

      Delete
  3. I have a file from AWS S3, how can I display/preview it in Salesforce? The URL is stored in Salesforce, need to display it as a preview. Used the above approach but the preview is not being displayed but the pdf is getting downloaded even after changing the settings as suggested

    ReplyDelete
  4. How to avoid showing the download icon in the modal displaying pdf ?

    ReplyDelete