How to Query Files and Display Them Using an LWC Component

Displaying files within a Lightning Web Component (LWC) can enhance your Salesforce application by enabling users to interact with documents, images, or attachments in a seamless, visually appealing way. Whether you’re working on a document management system, user profiles, or integration tracking, querying and displaying files can significantly improve user experience.

In this article, we’ll walk through how to query files stored in Salesforce and display them using a custom LWC component. We’ll cover key concepts like retrieving ContentDocument records, using Apex to expose this data, and finally designing an LWC that renders files attractively using Lightning Base Components or custom design.

1. Understanding How Files are Stored in Salesforce

Before diving into the code, it’s important to understand how files are managed within Salesforce’s data model. Salesforce uses the ContentDocument, ContentVersion, and ContentDocumentLink objects to store and manage files.

  • ContentDocument: The primary record for a file that has been uploaded.
  • ContentVersion: Represents a version of a document; the actual file is stored here.
  • ContentDocumentLink: Links a ContentDocument to another object such as an Account, Case, or Custom Object.

Therefore, to retrieve files related to any record, we start with the ContentDocumentLink object to find file relationships and then get the latest version for download or display.

2. Writing the Apex Controller

Since LWC cannot directly perform complex SOQL queries involving multiple objects, we will use an Apex controller to fetch the files based on a given record ID.


public with sharing class FileController {
    @AuraEnabled(cacheable=true)
    public static List<ContentVersion> getFiles(Id recordId) {
        List<ContentDocumentLink> links = [
            SELECT ContentDocumentId 
            FROM ContentDocumentLink 
            WHERE LinkedEntityId = :recordId
        ];
        
        List<Id> documentIds = new List<Id>();
        for(ContentDocumentLink link : links) {
            documentIds.add(link.ContentDocumentId);
        }

        return [
            SELECT Title, ContentDocumentId, VersionData, FileExtension, Id 
            FROM ContentVersion 
            WHERE ContentDocumentId IN :documentIds
            AND IsLatest = true
        ];
    }
}

This controller method will allow us to call getFiles from the LWC by passing the specific record ID and receiving a list of the latest file versions available for that record.

3. Creating the LWC Component

Next, we’ll create the actual LWC component that will call the Apex method and display each file dynamically.

The component will consist of three files:

  1. fileViewer.html – for layout
  2. fileViewer.js – for logic and data handling
  3. fileViewer.js-meta.xml – for metadata and exposure

fileViewer.html


<template>
    <template if:true={files}>
        <lightning-card title="Related Files" icon-name="standard:document">
            <template for:each={files} for:item="file">
                <div key={file.Id} class="slds-box slds-m-around_x-small">
                    <p><b>{file.Title}.{file.FileExtension}</b></p>
                    <lightning-button 
                        label="View File" 
                        onclick={handleFileClick} 
                        data-id={file.ContentDocumentId}
                        class="slds-m-top_small">
                    </lightning-button>
                </div>
            </template>
        </lightning-card>
    </template>
    <template if:true={error}>
        <p class="slds-text-color_error">{error}</p>
    </template>
</template>

fileViewer.js


import { LightningElement, api, wire } from 'lwc';
import getFiles from '@salesforce/apex/FileController.getFiles';

export default class FileViewer extends LightningElement {
    @api recordId;
    files;
    error;

    @wire(getFiles, { recordId: '$recordId' })
    wiredFiles({ error, data }) {
        if (data) {
            this.files = data;
            this.error = undefined;
        } else if (error) {
            this.error = error.body.message;
            this.files = undefined;
        }
    }

    handleFileClick(event) {
        const docId = event.target.dataset.id;
        window.open('/sfc/servlet.shepherd/document/download/' + docId, '_blank');
    }
}

fileViewer.js-meta.xml


<LightningComponentBundle xmlns="http://soap.sforce.com/2006/04/metadata">
    <apiVersion>58.0</apiVersion>
    <isExposed>true</isExposed>
    <targets>
        <target>lightning__RecordPage</target>
    </targets>
</LightningComponentBundle>

Now, you can place the fileViewer component on any record page using the Lightning App Builder and it will list all files related to that record. The user can view or download them with a simple click.

4. Display Enhancements and Previews

The example above provides functionality to download files, but what if you want to render previews like images or PDFs inline? That’s possible too!

Depending on the file type, you can use the ContentVersion’s VersionData or embed the file using an <iframe> or <img> tag.

<template for:each={files} for:item="file">
    <div key={file.Id} class="slds-box slds-m-around_x-small">
        <p><b>{file.Title}</b></p>
        <template if:true={isImage(file.FileExtension)}>
            <img src={'/sfc/servlet.shepherd/version/download/' + file.Id} alt="file image" style="max-width: 100%;" />
        </template>
    </div>
</template>

Here, you’ll need to add a helper method in your JavaScript to check whether the file is an image.


isImage(fileExt) {
    return ['jpg', 'jpeg', 'png', 'gif'].includes(fileExt.toLowerCase());
}

5. Security and Access Considerations

Every time you work with file data, especially in a multi-user environment like Salesforce, it’s important to consider access rights. Your Apex controller should run in sharing mode to respect record-level access rules unless you have specific reasons not to.

Also, be careful with exposing file download URLs. While the standard URL constructs from Salesforce work, they shouldn’t be sent to unauthorized users. Use Apex sharing and field-level security best practices while querying and exposing properties like VersionData.

6. Use Cases and Customization Ideas

This component opens the door to many possible real-world uses. Here are some ideas to inspire you:

  • User Profile Pictures: Show user-uploaded avatars on user or contact pages.
  • Proposal Attachments: Make attached PDFs viewable from the opportunity record.
  • Case Attachments: Automatically preview screenshots or documents submitted via support cases.

Conclusion

Adding file display capabilities to your LWC not only enhances user experience but also streamlines access to important documents and resources. By understanding how Salesforce stores files and using a mix of Apex and LWC, you can create sleek, practical file viewers.

Whether you’re building internal tools or customer-facing portals, querying and displaying ContentDocuments can dramatically improve usability. With just a few lines of well-structured code, your Lightning components can become powerful document management utilities.

Use this

You May Also Like