Skip to content
Development
Skill

/experience-ui-bundle-file-upload-generate

MUST activate when the project contains a uiBundles/*/src/ directory and the task involves uploading, attaching, or dropping files. Use this skill when adding file upload functionality to a UI bundle app. Provides progress tracking and Salesforce ContentVersion integration. This

From plugin
sf-skills
803161 skills6 agents10 commands3 MCP
Install
$ npx -y skills add forcedotcom/sf-skills --skill experience-ui-bundle-file-upload-generate --agent claude-code

How it fires

How this skill gets triggered: by you, by Claude, or both.

  • Fires itselfAuto-invocation. Claude auto-loads it when your prompt matches the work.Auto-invocation is when the right skill fires by itself at the right moment, driven by a FLOW.md router and a hook, instead of you invoking it by name. It is the difference between a skill being installed and a skill actually getting used.Read the full definition →
  • You can call itInvoke it directly when you want it.
  • Slash command/experience-ui-bundle-file-upload-generate

Context preview

The summary Claude sees to decide when to auto-load this skill.

MUST activate when the project contains a uiBundles/*/src/ directory and the task involves uploading, attaching, or dropping files. Use this skill when adding file upload functionality to a UI bundle app. Provides progress tracking and Salesforce ContentVersion integration. This

SKILL.md

experience-ui-bundle-file-upload-generate.SKILL.md
name: experience-ui-bundle-file-upload-generate
description: "MUST activate when the project contains a uiBundles/*/src/ directory and the task involves uploading, attaching, or dropping files. Use this skill when adding file upload functionality to a UI bundle app. Provides progress tracking and Salesforce ContentVersion integration. This feature provides programmatic APIs ONLY — build custom UI using the upload() API. ALWAYS use this instead of building file upload from scratch with FormData or XHR."
metadata:
  version: "1.0"
  relatedSkills:
    - "experience-ui-bundle-salesforce-data-access"
  cliTools:
    - tool: ["npm"]
      semver: ">=7.0.0"

File Upload API (workflow)

When the user wants file upload functionality in a React UI bundle, follow this workflow. This feature provides **APIs only** — you must build the UI components yourself using the provided APIs.

CRITICAL: This is an API-only package

The package exports **programmatic APIs**, not React components or hooks. You will:

  • Use the `upload()` function to handle file uploads with progress tracking
  • Build your own custom UI (file input, dropzone, progress bars, etc.)
  • Track upload progress through the `onProgress` callback

**Do NOT:**

  • Expect pre-built components like `<FileUpload />` — they are not exported
  • Try to import React hooks like `useFileUpload` — they are not exported
  • Look for dropzone components — they are not exported

The source code contains reference components for demonstration, but they are **not available** as imports. Use them as examples to build your own UI.

1. Install the package

npm install @salesforce/ui-bundle-template-feature-react-file-upload

Dependencies are automatically installed:

  • `@salesforce/ui-bundle` (API client)
  • `@salesforce/platform-sdk` (data SDK; the old `@salesforce/sdk-data` name is dead — see the `experience-ui-bundle-salesforce-data-access` skill)

2. Understand the three upload patterns

Pattern A: Basic upload (no record linking)

Upload files to Salesforce and get back `contentBodyId` for each file. No ContentVersion record is created.

**When to use:**

  • User wants to upload files first, then create/link them to a record later
  • Building a multi-step form where the record doesn't exist yet
  • Deferred record linking scenarios
import { upload } from "@salesforce/ui-bundle-template-feature-react-file-upload";

const results = await upload({
  files: [file1, file2],
  onProgress: (progress) => {
    console.log(`${progress.fileName}: ${progress.status} - ${progress.progress}%`);
  },
});

// results[0].contentBodyId: "069..." (always available)
// results[0].contentVersionId: undefined (no record linked)

Pattern B: Upload with immediate record linking

Upload files and immediately link them to an existing Salesforce record by creating ContentVersion records.

**When to use:**

  • Record already exists (Account, Opportunity, Case, etc.)
  • User wants files immediately attached to the record
  • Direct upload-and-attach scenarios
import { upload } from "@salesforce/ui-bundle-template-feature-react-file-upload";

const results = await upload({
  files: [file1, file2],
  recordId: "001xx000000yyyy", // Existing record ID
  onProgress: (progress) => {
    console.log(`${progress.fileName}: ${progress.status} - ${progress.progress}%`);
  },
});

// results[0].contentBodyId: "069..." (always available)
// results[0].contentVersionId: "068..." (linked to record)

Pattern C: Deferred record linking (record creation flow)

Upload files without a record, then link them after the record is created.

**When to use:**

  • Building a "create record with attachments" form
  • Record doesn't exist until form submission
  • Need to upload files before knowing the final record ID
import {
  upload,
  createContentVersion,
} from "@salesforce/ui-bundle-template-feature-react-file-upload";

// Step 1: Upload files (no recordId)
const uploadResults = await upload({
  files: [file1, file2],
  onProgress: (progress) => console.log(progress),
});

// Step 2: Create the record
const newRecordId = await createRecord(formData);

// Step 3: Link uploaded files to the new record
for (const file of uploadResults) {
  const contentVersionId = await createContentVersion(
    new File([""], file.fileName),
    file.contentBodyId,
    newRecordId,
  );
}

3. Build your custom UI

The package provides the backend — you build the frontend. Here's a minimal example:

import {
  upload,
  type FileUploadProgress,
} from "@salesforce/ui-bundle-template-feature-react-file-upload";
import { useState } from "react";

function CustomFileUpload({ recordId }: { recordId?: string }) {
  const [progress, setProgress] = useState<Map<string, FileUploadProgress>>(new Map());

  const handleFileSelect = async (event: React.ChangeEvent<HTMLInputElement>) => {
    const files = Array.from(event.target.files || []);

    await upload({
      files,
      recordId,
      onProgress: (fileProgress) => {
        setProgress((prev) => new Map(prev).set(fileProgress.fileName, fileProgress));
      },
    });
  };

  return (
    <div>
      <input type="file" multiple onChange={handleFileSelect} />

      {Array.from(progress.entries()).map(([fileName, fileProgress]) => (
        <div key={fileName}>
          {fileName}: {fileProgress.status} - {fileProgress.progress}%
          {fileProgress.error && <span>Error: {fileProgress.error}</span>}
        </div>
      ))}
    </div>
  );
}

4. Track upload progress

The `onProgress` callback fires multiple times for each file as it moves through stages:

| Status | When | Progress Value | | -------------- | ---------------------------------------------- | -------------------- | | `"pending"` | File queued for upload | `0` | | `"uploading"` | Upload in progress (XHR)

Read more
Ships withsf-skills

This repository provides a curated collection of Salesforce agent skills for building applications.

Get the whole plugin

Other skills on sf-skills.