Photo upload
A dashed drop zone that opens the camera or file picker, with a grid of thumbnails, file names and a remove button per photo.

- Version
- 1.0.0
- Updated
- Verified in Studio
- Not yet
- Author
- Ynias Bensch
- Tags
Overview
A photo upload for inspections and incident reports. A dashed zone in the theme colour invites people to take or choose a photo; on a phone it opens the camera, on a computer the file picker. Each photo appears below as a thumbnail with its file name and a remove button, and the header counts them.
The zone is the classic Add picture control, made transparent and laid over the design, so it accepts one photo at a time. Photos are kept in the collection colFileUploadFiles until you save them, for example to a SharePoint list or Dataverse table with an image column.
When to use
- Adding photos as evidence to an inspection, finding or request.
- Up to about ten photos per record.
When not to use
- Documents such as PDFs: use the attachments control in a form, which handles any file type.
- Many files at once: let people upload them in SharePoint or OneDrive and link the folder.
Before you paste
- Something that saves the photos, for example a Save button with
ForAll(colFileUploadFiles, Patch(Photos, Defaults(Photos), {Inspection: varInspection.ID, Name: Name, Image: Image})).
Controls used
| Control | Version |
|---|---|
| GroupContainer | 1.5.0 |
| ModernText | 1.0.0 |
| ModernIcon | 1.1.1 |
| AddMedia | 2.2.1 |
| Gallery | 2.15.0 |
| Image | 2.2.3 |
| ModernButton | 1.0.0 |
YAML
Works as is, with example data inline. Paste it into a screen or container; the steps are underHow to paste.
Not yet verified in Power Apps Studio. The control versions are drafted from documentation. If Studio flags one, see How to paste for the fix.
- FileUploadRoot:
Control: GroupContainer@1.5.0
Variant: AutoLayout
Properties:
BorderColor: =ColorFade(App.Theme.Colors.Darker70, 88%)
BorderThickness: =1
DropShadow: =DropShadow.Light
Fill: =Color.White
Height: =FileUploadHeader.Height + FileUploadZone.Height + If(FileUploadThumbs.Visible, FileUploadThumbs.Height, FileUploadEmpty.Height) + 2 * 12 + 40
LayoutDirection: =LayoutDirection.Vertical
LayoutGap: =12
PaddingBottom: =20
PaddingLeft: =20
PaddingRight: =20
PaddingTop: =20
RadiusBottomLeft: =16
RadiusBottomRight: =16
RadiusTopLeft: =16
RadiusTopRight: =16
Width: =Min(640, Parent.Width)
Children:
- FileUploadHeader:
Control: GroupContainer@1.5.0
Variant: AutoLayout
Properties:
AlignInContainer: =AlignInContainer.Stretch
DropShadow: =DropShadow.None
FillPortions: =0
Height: =26
LayoutAlignItems: =LayoutAlignItems.Center
LayoutDirection: =LayoutDirection.Horizontal
LayoutGap: =8
LayoutMinHeight: =26
LayoutMinWidth: =120
Children:
- FileUploadTitle:
Control: ModernText@1.0.0
Properties:
AccessibleLabel: =Self.Text
FillPortions: =1
FontWeight: =FontWeight.Semibold
Height: =26
LayoutMinHeight: =0
LayoutMinWidth: =80
PaddingBottom: =0
PaddingLeft: =0
PaddingTop: =0
Size: =16
Text: ="Photos"
Wrap: =false
- FileUploadCount:
Control: ModernText@1.0.0
Properties:
AccessibleLabel: =Self.Text
Align: =Align.Right
Color: =ColorFade(App.Theme.Colors.Darker70, 45%)
Height: =26
LayoutMinHeight: =0
LayoutMinWidth: =60
PaddingBottom: =0
PaddingRight: =0
PaddingTop: =0
Size: =13
Text: =CountRows(colFileUploadFiles) & If(CountRows(colFileUploadFiles) = 1, " photo", " photos")
Width: =96
Wrap: =false
- FileUploadZone:
Control: GroupContainer@1.5.0
Variant: ManualLayout
Properties:
AlignInContainer: =AlignInContainer.Stretch
DropShadow: =DropShadow.None
FillPortions: =0
Height: =128
LayoutMinHeight: =128
LayoutMinWidth: =120
Children:
- FileUploadFrame:
Control: ModernText@1.0.0
Properties:
AccessibleLabel: ="Drop zone for photos"
BorderColor: =ColorFade(App.Theme.Colors.Primary, 40%)
BorderStyle: =BorderStyle.Dashed
BorderThickness: =2
Fill: =ColorFade(App.Theme.Colors.Lighter80, 50%)
Height: =Parent.Height
RadiusBottomLeft: =12
RadiusBottomRight: =12
RadiusTopLeft: =12
RadiusTopRight: =12
Text: =""
Width: =Parent.Width
X: =0
Y: =0
- FileUploadIcon:
Control: ModernIcon@1.1.1
Properties:
AccessibleLabel: ="Add"
Fill: =Color.White
Height: =40
Icon: ="Add"
IconColor: =App.Theme.Colors.Primary
PaddingBottom: =10
PaddingLeft: =10
PaddingRight: =10
PaddingTop: =10
RadiusBottomLeft: =20
RadiusBottomRight: =20
RadiusTopLeft: =20
RadiusTopRight: =20
Width: =40
X: =(Parent.Width - Self.Width) / 2
Y: =18
- FileUploadPrompt:
Control: ModernText@1.0.0
Properties:
AccessibleLabel: =Self.Text
Align: =Align.Center
Color: =App.Theme.Colors.Primary
FontWeight: =FontWeight.Semibold
Height: =24
PaddingBottom: =0
PaddingTop: =0
Size: =14
Text: ="Take or choose a photo"
Width: =Parent.Width
Wrap: =false
X: =0
Y: =64
- FileUploadHint:
Control: ModernText@1.0.0
Properties:
AccessibleLabel: =Self.Text
Align: =Align.Center
Color: =ColorFade(App.Theme.Colors.Darker70, 45%)
Height: =20
PaddingBottom: =0
PaddingTop: =0
Size: =12
Text: ="JPG or PNG, one photo at a time"
Width: =Parent.Width
Wrap: =false
X: =0
Y: =88
- FileUploadPicker:
Control: AddMedia@2.2.1
Properties:
AccessibleLabel: ="Take or choose a photo"
BorderThickness: =0
ChangePictureText: =""
Color: =Color.Transparent
Fill: =Color.Transparent
FocusedBorderColor: =App.Theme.Colors.Primary
Height: =Parent.Height
HoverFill: =ColorFade(App.Theme.Colors.Primary, 94%)
OnChange: |
=If(
Not(IsBlank(Self.Media)),
Collect(colFileUploadFiles, {Name: Self.FileName, Image: Self.Media, Added: Now()});
Reset(Self)
)
PressedFill: =ColorFade(App.Theme.Colors.Primary, 88%)
Text: =""
UseMobileCamera: =true
Width: =Parent.Width
X: =0
Y: =0
- FileUploadThumbs:
Control: Gallery@2.15.0
Variant: Vertical
Properties:
AccessibleLabel: ="Added photos"
AlignInContainer: =AlignInContainer.Stretch
BorderThickness: =0
FillPortions: =0
Height: =RoundUp(Self.AllItemsCount / Max(2, RoundDown(Self.Width / 150, 0)), 0) * Self.TemplateHeight
Items: =colFileUploadFiles
LayoutMinHeight: =0
LayoutMinWidth: =120
ShowScrollbar: =false
TabIndex: =0
TemplatePadding: =0
TemplateSize: =148
Visible: =Self.AllItemsCount > 0
WrapCount: =Max(2, RoundDown(Self.Width / 150, 0))
Children:
- FileUploadThumb:
Control: Image@2.2.3
Properties:
AccessibleLabel: =ThisItem.Name
Height: =Parent.TemplateHeight - 36
Image: =ThisItem.Image
ImagePosition: =ImagePosition.Fill
RadiusBottomLeft: =10
RadiusBottomRight: =10
RadiusTopLeft: =10
RadiusTopRight: =10
TabIndex: =0
Width: =Parent.TemplateWidth - 10
X: =0
Y: =0
- FileUploadName:
Control: ModernText@1.0.0
Properties:
AccessibleLabel: =Self.Text
Color: =ColorFade(App.Theme.Colors.Darker70, 30%)
Height: =24
PaddingBottom: =0
PaddingLeft: =0
PaddingTop: =0
Size: =12
Text: =ThisItem.Name
Width: =Parent.TemplateWidth - 10
Wrap: =false
X: =0
Y: =Parent.TemplateHeight - 32
- FileUploadRemove:
Control: ModernButton@1.0.0
Properties:
AccessibleLabel: ="Remove " & ThisItem.Name
Appearance: =ButtonAppearance.Secondary
Height: =28
Icon: ="Dismiss"
Layout: =ButtonLayout.IconOnly
OnSelect: =Remove(colFileUploadFiles, ThisItem)
RadiusBottomLeft: =14
RadiusBottomRight: =14
RadiusTopLeft: =14
RadiusTopRight: =14
Size: =12
Tooltip: ="Remove"
Width: =28
X: =Parent.TemplateWidth - Self.Width - 16
Y: =6
- FileUploadEmpty:
Control: ModernText@1.0.0
Properties:
AccessibleLabel: =Self.Text
AlignInContainer: =AlignInContainer.Stretch
Color: =ColorFade(App.Theme.Colors.Darker70, 45%)
Height: =28
LayoutMinHeight: =0
LayoutMinWidth: =80
PaddingLeft: =0
Size: =13
Text: ="No photos yet"
Visible: =Not(FileUploadThumbs.Visible)
Wrap: =false- FileUploadRoot:
Control: GroupContainer@1.5.0
Variant: AutoLayout
Properties:
BorderColor: =ColorFade(App.Theme.Colors.Darker70, 88%)
BorderThickness: =1
DropShadow: =DropShadow.Light
Fill: =Color.White
Height: =FileUploadHeader.Height + FileUploadZone.Height + If(FileUploadThumbs.Visible, FileUploadThumbs.Height, FileUploadEmpty.Height) + 2 * 12 + 40
LayoutDirection: =LayoutDirection.Vertical
LayoutGap: =12
PaddingBottom: =20
PaddingLeft: =20
PaddingRight: =20
PaddingTop: =20
RadiusBottomLeft: =16
RadiusBottomRight: =16
RadiusTopLeft: =16
RadiusTopRight: =16
Width: =Min(640, Parent.Width)
Children:
- FileUploadHeader:
Control: GroupContainer@1.5.0
Variant: AutoLayout
Properties:
AlignInContainer: =AlignInContainer.Stretch
DropShadow: =DropShadow.None
FillPortions: =0
Height: =26
LayoutAlignItems: =LayoutAlignItems.Center
LayoutDirection: =LayoutDirection.Horizontal
LayoutGap: =8
LayoutMinHeight: =26
LayoutMinWidth: =120
Children:
- FileUploadTitle:
Control: ModernText@1.0.0
Properties:
AccessibleLabel: =Self.Text
FillPortions: =1
FontWeight: =FontWeight.Semibold
Height: =26
LayoutMinHeight: =0
LayoutMinWidth: =80
PaddingBottom: =0
PaddingLeft: =0
PaddingTop: =0
Size: =16
Text: ="Photos"
Wrap: =false
- FileUploadCount:
Control: ModernText@1.0.0
Properties:
AccessibleLabel: =Self.Text
Align: =Align.Right
Color: =ColorFade(App.Theme.Colors.Darker70, 45%)
Height: =26
LayoutMinHeight: =0
LayoutMinWidth: =60
PaddingBottom: =0
PaddingRight: =0
PaddingTop: =0
Size: =13
Text: =CountRows(colFileUploadFiles) & If(CountRows(colFileUploadFiles) = 1, " photo", " photos")
Width: =96
Wrap: =false
- FileUploadZone:
Control: GroupContainer@1.5.0
Variant: ManualLayout
Properties:
AlignInContainer: =AlignInContainer.Stretch
DropShadow: =DropShadow.None
FillPortions: =0
Height: =128
LayoutMinHeight: =128
LayoutMinWidth: =120
Children:
- FileUploadFrame:
Control: ModernText@1.0.0
Properties:
AccessibleLabel: ="Drop zone for photos"
BorderColor: =ColorFade(App.Theme.Colors.Primary, 40%)
BorderStyle: =BorderStyle.Dashed
BorderThickness: =2
Fill: =ColorFade(App.Theme.Colors.Lighter80, 50%)
Height: =Parent.Height
RadiusBottomLeft: =12
RadiusBottomRight: =12
RadiusTopLeft: =12
RadiusTopRight: =12
Text: =""
Width: =Parent.Width
X: =0
Y: =0
- FileUploadIcon:
Control: ModernIcon@1.1.1
Properties:
AccessibleLabel: ="Add"
Fill: =Color.White
Height: =40
Icon: ="Add"
IconColor: =App.Theme.Colors.Primary
PaddingBottom: =10
PaddingLeft: =10
PaddingRight: =10
PaddingTop: =10
RadiusBottomLeft: =20
RadiusBottomRight: =20
RadiusTopLeft: =20
RadiusTopRight: =20
Width: =40
X: =(Parent.Width - Self.Width) / 2
Y: =18
- FileUploadPrompt:
Control: ModernText@1.0.0
Properties:
AccessibleLabel: =Self.Text
Align: =Align.Center
Color: =App.Theme.Colors.Primary
FontWeight: =FontWeight.Semibold
Height: =24
PaddingBottom: =0
PaddingTop: =0
Size: =14
Text: ="Take or choose a photo"
Width: =Parent.Width
Wrap: =false
X: =0
Y: =64
- FileUploadHint:
Control: ModernText@1.0.0
Properties:
AccessibleLabel: =Self.Text
Align: =Align.Center
Color: =ColorFade(App.Theme.Colors.Darker70, 45%)
Height: =20
PaddingBottom: =0
PaddingTop: =0
Size: =12
Text: ="JPG or PNG, one photo at a time"
Width: =Parent.Width
Wrap: =false
X: =0
Y: =88
- FileUploadPicker:
Control: AddMedia@2.2.1
Properties:
AccessibleLabel: ="Take or choose a photo"
BorderThickness: =0
ChangePictureText: =""
Color: =Color.Transparent
Fill: =Color.Transparent
FocusedBorderColor: =App.Theme.Colors.Primary
Height: =Parent.Height
HoverFill: =ColorFade(App.Theme.Colors.Primary, 94%)
OnChange: |
=If(
Not(IsBlank(Self.Media)),
Collect(colFileUploadFiles, {Name: Self.FileName, Image: Self.Media, Added: Now()});
Reset(Self)
)
PressedFill: =ColorFade(App.Theme.Colors.Primary, 88%)
Text: =""
UseMobileCamera: =true
Width: =Parent.Width
X: =0
Y: =0
- FileUploadThumbs:
Control: Gallery@2.15.0
Variant: Vertical
Properties:
AccessibleLabel: ="Added photos"
AlignInContainer: =AlignInContainer.Stretch
BorderThickness: =0
FillPortions: =0
Height: =RoundUp(Self.AllItemsCount / Max(2, RoundDown(Self.Width / 150, 0)), 0) * Self.TemplateHeight
Items: =colFileUploadFiles
LayoutMinHeight: =0
LayoutMinWidth: =120
ShowScrollbar: =false
TabIndex: =0
TemplatePadding: =0
TemplateSize: =148
Visible: =Self.AllItemsCount > 0
WrapCount: =Max(2, RoundDown(Self.Width / 150, 0))
Children:
- FileUploadThumb:
Control: Image@2.2.3
Properties:
AccessibleLabel: =ThisItem.Name
Height: =Parent.TemplateHeight - 36
Image: =ThisItem.Image
ImagePosition: =ImagePosition.Fill
RadiusBottomLeft: =10
RadiusBottomRight: =10
RadiusTopLeft: =10
RadiusTopRight: =10
TabIndex: =0
Width: =Parent.TemplateWidth - 10
X: =0
Y: =0
- FileUploadName:
Control: ModernText@1.0.0
Properties:
AccessibleLabel: =Self.Text
Color: =ColorFade(App.Theme.Colors.Darker70, 30%)
Height: =24
PaddingBottom: =0
PaddingLeft: =0
PaddingTop: =0
Size: =12
Text: =ThisItem.Name
Width: =Parent.TemplateWidth - 10
Wrap: =false
X: =0
Y: =Parent.TemplateHeight - 32
- FileUploadRemove:
Control: ModernButton@1.0.0
Properties:
AccessibleLabel: ="Remove " & ThisItem.Name
Appearance: =ButtonAppearance.Secondary
Height: =28
Icon: ="Dismiss"
Layout: =ButtonLayout.IconOnly
OnSelect: =Remove(colFileUploadFiles, ThisItem)
RadiusBottomLeft: =14
RadiusBottomRight: =14
RadiusTopLeft: =14
RadiusTopRight: =14
Size: =12
Tooltip: ="Remove"
Width: =28
X: =Parent.TemplateWidth - Self.Width - 16
Y: =6
- FileUploadEmpty:
Control: ModernText@1.0.0
Properties:
AccessibleLabel: =Self.Text
AlignInContainer: =AlignInContainer.Stretch
Color: =ColorFade(App.Theme.Colors.Darker70, 45%)
Height: =28
LayoutMinHeight: =0
LayoutMinWidth: =80
PaddingLeft: =0
Size: =13
Text: ="No photos yet"
Visible: =Not(FileUploadThumbs.Visible)
Wrap: =false
Make it yours
The example runs on sample data. Replace these properties to connect it to your app.
Items on FileUploadThumbs
The photos to show. The picker adds to the collection colFileUploadFiles; sort or filter it here.
In the YAML
Items: =colFileUploadFilesYour version
Items: =Sort(colFileUploadFiles, Added, SortOrder.Descending)OnChange on FileUploadPicker
What happens with a new photo. Keep the Reset, so the zone is ready for the next one; add a size or count check if you need one.
In the YAML
OnChange: |
=If(
Not(IsBlank(Self.Media)),
Collect(colFileUploadFiles, {Name: Self.FileName, Image: Self.Media, Added: Now()});
Reset(Self)
)Your version
OnChange: |
=If(
CountRows(colFileUploadFiles) >= 10,
Notify("You can add up to 10 photos.", NotificationType.Warning),
Collect(colFileUploadFiles, {Name: Self.FileName, Image: Self.Media, Added: Now()})
);
Reset(Self)How to paste
- Open your app in Power Apps Studio.
- Check that modern controls are on: Settings → Updates → Modern controls and themes. In newer environments they are on by default.
- Copy the YAML with the Copy button above the code.
- In the Tree view, select the screen or container that should hold the component and press Ctrl+V. The whole component arrives as one container,
FileUploadRoot, with all its controls inside. - Work through Make it yours to swap the example data for your own.
Where your version of Studio has a code view for a control or screen, you can paste the same YAML there instead of into the tree view.
When Studio flags a control version
Each control in the YAML names its version, like Control: ModernButton@1.0.0. If that version is newer than what your environment knows, Studio falls back to the version it has or flags the control. All controls of one type in an app must also use the same version, so an app that already has an older ModernButton can reject the paste. To fix it, insert the same control from the Insert pane, copy it as code, note the version on its Control: line, update every matching line in the YAML and paste again.
Missing a component, or did this one not paste cleanly? Suggest a component or report an issue.