Upload files to SharePoint document libraries with validation, preview, and metadata display. Supports multiple file types and size restrictions.
This component requires Modern controls to be enabled. Settings → Updates → Preview → Modern controls and themes
Click to select files
or drag and drop
Accepted: All file types
Max size: 10 MB • Max files: 3
Budget_Report_2024.xlsx
2.4 MB
Team_Photo.jpg
3.8 MB
Large_Video_File.mp4
15.2 MB
FailedFile exceeds 10 MB limit
Allowed File Types
Max File Size
Max Files
Show Validation
Component Features
A documents panel for Canvas apps: a drag-and-drop staging area, a list of the files that already exist, and per-row preview, download and delete actions.
No connectors required. The component queries nothing and writes nothing. It stages files and raises events, and your app decides what a save or a delete means. That is what lets it drop into any app and work immediately against the sample rows.
Worth reading before the property list, because it is the shortest route to knowing whether this is the component you want.
OnSave hands you the staged files. Writing them to
SharePoint, Dataverse or anywhere else is yours, normally through a Flow. The
recipe below is a complete working one.OnDelete tells you which row was tapped. A document
already in a library is not the component's to remove.Items is an input. The component never sees your data
source, which is why it has no connector dependency and no delegation limits
of its own.AcceptedText prints a hint. The
underlying Attachments control enforces nothing about type, so a real
restriction belongs in your Flow.cmpFileUpload.yamlThen three things that a paste does not carry, all of which are Power Apps behaviour rather than anything about this component:
Width by hand. A pasted instance arrives at 600
regardless of what the property says. Height survives; Width does not.
Reproduced Studio to Studio by duplicating a screen.Height to DesiredHeight, or drop the instance in an AutoLayout
container and let it stretch.Style, Colors and TypeColors explicitly if you are customising
them. A Record or Table input's default does not reach a pasted instance.
The component Coalesces every read against its documented defaults, so
omitting them is safe, but a half-filled record is not.Everything below is optional. This renders a working panel against sample data:
Height: =cmpFileUpload_1.DesiredHeight
Items: =colMyDocs
colMyDocs needs the columns Id, Name, SizeBytes, UploadedOn,
UploadedBy, Ext. SizeBytes is a number of bytes rather than a formatted
string, because the footer totals it.
| Property | Type | Default | Description |
|---|---|---|---|
AcceptedText | Text | "PDF, DOC, XLSX, PNG" | File types named in the hint line under the dropzone. The size limit is appended from MaxFileSize, so do not type it here. |
AllowDelete | Boolean | true | PERMISSION. The bin on each existing document row. |
AllowUpload | Boolean | true | PERMISSION AND LAYOUT AT ONCE. The dropzone and the Upload and Cancel buttons. |
Colors | Record | see below | All ten keys are required. A missing key resolves blank and paints nothing with no error. |
HeaderText | Text | "Documents" | Heading above the dropzone. ShowHeader false hides it. |
Items | Table | see below | Documents that ALREADY EXIST. The component does not query anything, so it carries no connector dependency. |
MaxFileSize | Number | 25 | Megabytes. Shown in the hint line AND passed to the Attachments control's MaxAttachmentSize, so it is genuinely enforced at the point of staging. |
MaxFiles | Number | 5 | Cap on STAGED files, enforced in OnAddFile and passed to the Attachments control. Existing documents in Items are not counted against it. |
ShowFooter | Boolean | true | The count and total size line, and the Download All link. |
ShowHeader | Boolean | false | The HeaderText line. |
ShowRowActions | Boolean | true | The view, download and bin icons on each existing document row. |
Style | Record | see below | All eleven keys are required. DropHeight is the dashed zone. |
TypeColors | Table | see below | Maps a file extension to the colour of its tile. Ext is matched against Items.Ext case insensitively. |
| Output | Type | Description |
|---|---|---|
DesiredHeight | Number | Header plus dropzone plus both lists plus footer for the current flags and row counts. Bind the instance Height to this, or let a parent AutoLayout stretch the instance instead. |
DocCount | Number | Row count of Items, the documents that already exist. Does not include staged files. |
StagedCount | Number | Number of files staged but not yet saved. |
StagedFiles | Table | The staged attachments, in whatever shape the Attachments control produced. Read this inside OnSave and hand it to a Flow. It is NOT cleared on save, see OnSave. |
StagedFilesData | Table | A TABLE of { Name, ContentBytes } with clean base64. POPULATED WHEN UPLOAD IS PRESSED and only meaningful inside the OnSave handler. Use this when your Flow takes a TEXT parameter. |
TappedId | Text | Id of the document whose row action was last tapped. Only meaningful inside OnView, OnDownload or OnDelete. |
TappedName | Text | Name of the document whose row action was last tapped. |
TotalBytes | Number | Sum of SizeBytes across Items. Staged files are excluded, because the Attachments control does not expose a reliable size. |
TotalText | Text | TotalBytes formatted the same way a row is, KB under a megabyte and MB above. |
| Event | Fires when | Read in handler |
|---|---|---|
OnCancel | Cancel is tapped, after the staged list is cleared | — |
OnDelete | The bin icon on an existing document is tapped | TappedId, TappedName |
OnDownload | The download icon is tapped | TappedId, TappedName |
OnDownloadAll | The footer Download All link is tapped | DocCount |
OnSave | Upload is tapped | StagedFiles, StagedFilesData, StagedCount |
OnView | The eye icon is tapped | TappedId, TappedName |
The complete working path, including the part that silently fails if you skip it.
The Attachments control does not give you file bytes. Its Value is an
appres://blobmanager/... link, an internal temporary Power Apps identifier.
Power Automate cannot resolve it, so passing it to a Flow writes a zero byte
file with no error anywhere.
Which output you send depends on your Flow's trigger:
| Your Flow trigger takes | Send this | Why |
|---|---|---|
| A File input on the Power Apps (V2) trigger | StagedFiles | Power Apps resolves the blob natively. No base64 needed. |
| A Text input | StagedFilesData | A table of { Name, ContentBytes }. Loop it with ForAll, or wrap it in JSON() for a whole-batch Flow. |
The File input is the easiest and is what the recipe below uses.
All three are only meaningful inside OnSave. StagedFilesData in particular
is assembled at the moment Upload is pressed, so reading it anywhere else returns
the previous batch or nothing. Power Fx will not let a component build binary data
outside a behaviour formula, so it cannot be assembled continuously the way the
other outputs are. In practice this is invisible: OnSave is the only place you
would ever read it.
Trigger: Power Apps (V2) with a File input. Note that File inputs are optional by default while every other type is required, which is what makes the folder-creation call below work.
Then Create file:
| Field | Value |
|---|---|
| Site Address | your site |
| Folder Path | your library |
| File Name | triggerBody()['file']['name'] |
| File Content | triggerBody()['file']['contentBytes'] |
Finish with Respond to a PowerApp or flow returning one Text output. This is
not decoration: without a Respond action Power Apps does not wait for the
Flow, so your refresh runs before the file exists and IfError cannot catch
anything.
=Set(gblBusy, true);
Clear(colFailed);
ForAll(
cmpFileUpload_1.StagedFiles As f,
If(
IsError(
UploadDocument.Run({ file: { name: f.Name, contentBytes: f.Value } })
),
Collect(colFailed, { Name: f.Name })
)
);
Set(gblBusy, false);
If(
IsEmpty(colFailed),
Refresh(MyLibrary);
ClearCollect(colMyDocs, <your projection>);
Reset(cmpFileUpload_1);
Notify("Uploaded.", NotificationType.Success),
Notify(
CountRows(colFailed) & " failed: " & Concat(colFailed, Name, ", "),
NotificationType.Error
)
)
Three things about the order.
Reset() is last and only on success. The component deliberately keeps the
staged files after OnSave so it still holds the only copy until the write is
confirmed. Reset() clears both the collection and the Attachments control.
Refresh() before the re-read is not optional. Power Apps caches the source, so
without it the files you just uploaded are not in the collection and it looks
like nothing happened.
IsError() rather than IfError() returns a Boolean whatever the Flow returns,
so the formula survives adding or removing the Respond action.
A document library returns folders and files in one table, so filter the folders out or every folder renders as a document row:
=ClearCollect(
colMyDocs,
ForAll(
Filter(MyLibrary, '{IsFolder}' = false) As d,
{
Id: Text(d.ID),
Name: d.'{FilenameWithExtension}',
SizeBytes: 0,
UploadedOn: d.Modified,
UploadedBy: d.'Created By'.DisplayName,
Ext: Lower(Last(Split(d.'{FilenameWithExtension}', ".")).Value)
}
)
)
There is no file size column. The SharePoint connector does not expose one to
Power Apps, so SizeBytes is 0 and the footer total reads 0 KB. If size
matters, add a Number column to the library and write it from the Flow;
length(triggerBody()['file']['contentBytes']) is about 4/3 of the true size and
close enough for a display string.
Projected with ForAll building a record rather than ShowColumns and
RenameColumns, because those take a column name and whether it is written
bare or quoted depends on an app level setting.
{Link} is on the record, so neither needs a Flow:
OnView: =Launch(LookUp(MyLibrary, ID = Value(cmpFileUpload_1.TappedId)).'{Link}')
OnDownload: =Download(LookUp(MyLibrary, ID = Value(cmpFileUpload_1.TappedId)).'{Link}')
AllowUpload: =true
AllowDelete: =gblIsAdmin
Two independent switches, and all four combinations are useful:
AllowUpload | AllowDelete | What the user gets |
|---|---|---|
| true | true | Everything |
| true | false | Upload, view, download. Cannot remove anything. |
| false | true | No dropzone, but can still delete |
| false | false | Read, preview and download only |
The second row is the common one: everyone contributes, only an owner removes. The third is usually layout rather than permission, for when there is no room for a dropzone or uploading lives behind a button elsewhere on the screen.
ShowRowActions is separate again. It is the layout switch for the whole icon
cluster, where AllowDelete is the permission for the bin inside it. Hiding the
icons to save space and forbidding deletion are different decisions.
| Enforced file size | MaxFileSize, passed to the control. Decimal MB, so 1,000,000 bytes. |
| Platform ceiling | Around 50MB per file regardless of MaxFileSize, with failures earlier on slow connections. |
| Staged file count | MaxFiles. Existing documents do not count against it. |
| File type | Not enforced. AcceptedText is a hint only. |
| Instances per screen | One. The staged list lives in a component-scoped global, so two instances on a screen share it. |
The row and footer size labels divide by 1,048,576 (binary MiB) while the upload limit counts decimal MB. They disagree by about 5%, so a file displaying as "23.9 MB" can still be rejected at a limit of 25. This is deliberate: file managers report binary and upload limits are quoted decimal, and matching them would make one of the two wrong.
AccessibleLabel on modern controls and
by Tooltip on classic ones, which have no AccessibleLabel property.TabIndex: -1 and out of the tab order. Only the buttons
underneath them take focus.One email when something new ships. Unsubscribe anytime.
Use the toolbar to format · or type markdown directly