SfSkills Domains Docs GitHub

Home / lwc / file-upload-patterns

file-upload-patterns

Upload files in LWC: lightning-file-upload, manual multipart, large-file chunked upload, and ContentDocument associations. NOT for ContentDocument query patterns — use data/salesforce-files-architecture. Trigger keywords: file upload, lightning-file-upload, uploadfinished, FileReader, base64, ContentVersion, ContentDocumentLink, ShareType, Visibility, VersionData, FirstPublishLocationId, Apex heap, guest user upload.

Open the skill folder on GitHub


LWC File Upload Patterns

Getting bytes from a browser into Salesforce Files, and attaching the result to the right record. The whole domain turns on one question — does a record id exist at the moment of upload — and on one limit, the Apex heap, which decides whether the bytes may pass through Apex at all.


Adoption Signals

Any file intake UI. Choose the tier by whether a record id exists, by file size, and by who must see the result.

SignalTier
Record already saved, no pre-processing neededlightning-file-upload
No record id yet, or the bytes must be inspected or transformed firstcustom input → FileReader → Apex
File larger than a couple of megabyteslightning-file-upload — the Apex path cannot carry it
Guest or Experience Cloud audienceNeither, until the access design is settled first

The guide routes the first two itself: lightning-file-upload is "a file selector for uploading a file that's associated with a record ID. To upload a file without a record ID, use lightning-input with type="file" instead." (lwc_guide base-components-all L4603).


Before Starting


Questions to Ask Before Configuring

Ask these before writing markup. Each answer closes a branch that is expensive to reverse once the component ships, and each one traces to a gotcha in references/gotchas.md.

AskWhy it mattersWhat a good answer adds
"Does the parent record exist when the user picks the file?"It is the whole tier decision. With a record id the platform does the association; without one you own itThe tier, and for the Apex path the moment at which the link is created (gotcha 1)
"What is the biggest file this has to accept in the next two years?"Apex heap is 6 MB synchronous (apexdev L19577) and base64 adds about 37 % (object_reference L79833); past a couple of megabytes the Apex path is not available at any chunk sizeA hard cap written into the Apex controller, not only the JavaScript (gotchas 5, 6)
"Who must be able to open the file once it is stored — internal staff only, or a customer?"ShareType and Visibility decide it, and AllUsers on a record surfaced externally is how internal documents leakExplicit values at every ContentDocumentLink insert site (gotchas 3, 4)
"Will the file ever need to move to a different parent record?"FirstPublishLocationId is set only on first publish and cannot be changed laterWhether the one-statement form is safe or explicit links are required (gotcha 2)
"Which surface is this component on — Lightning Experience, an LWR site, a standalone app?"Confirmation UX differs: lightning/platformShowToastEvent is not supported in LWR sites or standalone apps (lwc_guide base-components-patterns L4773)The confirmation mechanism, chosen rather than defaulted (gotcha 9)
"Which file types must be refused, and what happens if a refused file gets through?"accept is a picker filter; only a server-side check on decoded bytes rejects anythingThe magic-number allowlist and the Apex rejection path (anti-pattern 5)
"Which permission set grants this, and does it include Apex class access?"Class access is a separate grant: an @AuraEnabled method is reachable only when the profile or a permission set allows the class (lwc_guide apex-security L7436)A permission set shipped in the same deployment as the component (gotcha 10)

What a proper configuration adds over just wiring an uploader: the file is visible on the record because the link was a deliberate insert rather than a hoped-for side effect, its audience is a chosen value rather than an inherited one, the size ceiling is enforced where a browser cannot bypass it, and the Jest and Apex tests fail when any of those three regress.


  1. Answer the record-id question first. If the record exists, build Bundle A from

    references/code-examples.md — lightning-file-upload with record-id, an uploadfinished handler, and no Apex at all. Stop here unless something below forces the Apex path.

  2. If the Apex path is forced, size it before writing it. Take the largest real file, add

    ~37 % for base64 (object_reference L79833), and check it against the 6 MB synchronous heap (apexdev L19577). If it does not clear that with margin, the requirement is wrong, not the implementation — go back to step 1 or to a direct API upload.

  3. Build Bundle B as a pair. references/code-examples.md § B: client size guard →

    FileReader → @AuraEnabled (never cacheable=true) → ContentVersion → ContentDocumentLink with explicit ShareType and Visibility → return the ContentDocumentId. Validate the decoded bytes, not the extension.

  4. Write both tests before deploying. The Jest suite dispatches a synthetic

    uploadfinished event and mocks FileReader and the Apex import; the Apex test uses a four-byte Blob and asserts the ContentDocumentLink row exists with the intended ShareType. Both are in references/code-examples.md §§ A.5, B.6, B.7.

  5. Run the checker over the source tree:

    python3 scripts/check_file_upload_patterns.py --manifest-dir force-app/main/default. Use --strict in CI to fail on warnings. It catches an orphan ContentVersion, a cacheable upload method, a missing ShareType, an unguarded base64 payload and a lightning-file-upload with neither a record-id nor a documented alternative.

  6. Deploy Apex before the components, and the permission set with class access in the

    same release. @salesforce/* imports are validated against org metadata (lwc_guide get-started-oss L126), so the component will not deploy against a class that is not there yet.

  7. Verify against ContentDocumentLink, not against the file. Run the filtered query in

    references/code-examples.md § Verification. An unfiltered ContentDocumentLink query needs View All Data from API 59.0 on (object_reference L77161-L77162), so filter by LinkedEntityId and confirm ShareType, Visibility and the raw byte count.


Key Considerations

FactSourceConsequence
Apex total heap: 6 MB synchronous, 12 MB asynchronousapexdev L19577An @AuraEnabled upload runs in the synchronous bucket
Base64 conversion "increases the document size by approximately 37%"object_reference L79833The 4/3 arithmetic understates it; budget from the documented figure
Maximum file size uploadable via the SOAP API is 50 MB, measured after base64 conversionobject_reference L79771-L79774The API ceiling and the component ceiling are different numbers
ContentDocumentLink.ShareType is Required; C cannot be created from an Apex triggerobject_reference L77269, L77276-L77277Set it at every insert; move trigger-based collaborator links elsewhere
Visibility values are AllUsers, InternalUsers, SharedUsers; external users can only set AllUsersobject_reference L77302-L77317Write it explicitly; default to InternalUsers in code
FirstPublishLocationId shares the file in a single transaction, but is set only on first publishobject_reference L79403-L79409Use explicit links wherever the parent may change
ContentDocument has no create() callobject_reference L76648-L76649You cannot manufacture a document to repair an orphan version
A cacheable Apex method "must only get data, it can't mutate (change) data"lwc_guide apex-result-caching L7254An upload method is never cacheable=true
lightning/platformShowToastEvent is unsupported in LWR sites and standalone appslwc_guide base-components-patterns L4773Choose the confirmation mechanism from the target

Virus scanning is not part of any of this. UNVERIFIED (2026-09-05): the claim that Salesforce performs no automatic malware scan on uploaded Files is not stated in the grounding corpus (Object Reference, Apex Developer Guide, LWC Developer Guide, App Limits cheat sheet); treat scanning as a separate design and see security/file-upload-virus-scanning.


Worked Examples (see references/examples.md)

Common Gotchas (see references/gotchas.md)

Top LLM Anti-Patterns (full list in references/llm-anti-patterns.md)


Reference Files

FileRead it when
references/code-examples.mdYou are writing the bundle: Bundle A (lightning-file-upload + uploadfinished + toast), Bundle B (custom input → FileReader → Apex → ContentVersion + ContentDocumentLink), both Jest suites, the Apex test, js-meta.xml, package.xml, deploy order and the verification SOQL
references/gotchas.mdA file uploaded successfully and something about it is wrong — invisible on the record, visible to the wrong audience, failing on heap, or unqueryable
references/llm-anti-patterns.mdYou are reviewing generated upload code, or want the detection hints the checker implements
references/well-architected.mdYou need the pillar framing, the tier tradeoff argument, or the sourced claims list
templates/file-upload-patterns-template.mdYou are recording the tier decision, the size budget and the sharing choice for review
scripts/check_file_upload_patterns.pyBefore deploy: run it with --manifest-dir over the LWC and Apex source tree, --strict in CI


Official Sources Used

See references/well-architected.md § Official Sources Used for the full list with the claim each source supports.

Package files