Attachments belong in the form — and 5.1 hides them behind three defaults
Attachments have always been the awkward part of an ArcGIS editing app. The form was one
thing — fields, domains, Arcade-driven visibility, a layout you configured — and attachments
were a separate panel bolted on beside it, with no awareness of anything the inspector had just typed.
Maps SDK for JavaScript 5.1, released June 2026, closes that gap: attachments become a
first-class form element inside the same FormTemplate as everything else.
The feature is genuinely good. It also ships behind three defaults — two that render the element invisible and a third that silently disables its best property — plus a keyword rule that can hide every attachment already sitting on your layer. None of them fail loudly. This article is mostly about them.
AttachmentElement to a form template is not enough on its own.
Default one:
FormTemplate.supportsAttachmentElements is
false — without setting it, the form ignores attachment elements no matter
which editor you use. Default two: on the deprecated Editor widget,
formSystem is legacy and renders none of the new elements.
Default three: useOriginalFilename is
true, which makes filenameExpression inert. None of
the three throws or warns. You get a form that validates and is quietly missing what you configured.What 5.1 actually added
Three things, and the middle one exists to serve the first.
First, AttachmentElement — a form element you place in
FormTemplate.elements alongside your field and group elements. Second, a family
of typed attachment inputs that constrain what an attachment may be:
ImageInput, DocumentInput,
AudioInput, VideoInput,
SignatureInput, and the general
AttachmentInput — which, as we will see, is the one that saves you on an
existing layer. Third, MultipleChoiceInput, which finally gives forms a real
checkbox-and-radio control instead of a coded-value domain pressed into service.
The 5.1 release notes frame it as attachment authoring being “integrated into the same configurable
form experience, allowing attachment management to follow the overall form layout and behavior.” That
is the point. An attachment element obeys the same
visibilityExpression and editableExpression Arcade
hooks as any other element, so the photo slot can appear only when the inspector marks a defect —
something that previously meant hand-building the attachment UI yourself.
The element, property by property
The API surface is small and most of it earns its place:
| Property | What it does | Why you care |
|---|---|---|
input |
The typed input — image, document, audio, video, signature, or general. | Constrains the picker. It is a UX constraint, not a content check — see the security note below. |
minAttachmentCount / maxAttachmentCount |
Required minimum and permitted maximum. | Validation you used to write by hand, and usually wrote inconsistently. |
filenameExpression |
Arcade expression that generates the stored filename. | The quiet winner — but inert unless you also change a default. See below. |
useOriginalFilename / allowUserRename |
Keep the uploaded name; let the user edit it. Both default to true. |
These two decide whether filenameExpression ever runs. |
displayFilename |
Shows or hides the filename in the form. | Cosmetic, but worth turning on while you are testing a filename expression. |
attachmentKeyword |
Tags the attachment with a keyword. | Lets two elements write to distinct buckets — before photos and after photos. Also the property most likely to hide your existing data. |
visibilityExpression / editableExpression |
Arcade-driven show and lock. | Conditional capture without custom UI code. |
The code
The element autocasts its input, so you rarely import the input classes
directly:
import FormTemplate from "@arcgis/core/form/FormTemplate.js";
import AttachmentElement from "@arcgis/core/form/elements/AttachmentElement.js";
import FeatureLayer from "@arcgis/core/layers/FeatureLayer.js";
const photos = new AttachmentElement({
label: "Defect photos",
input: { // autocasts to ImageInput
type: "image",
maxImageSize: 1600 // PIXELS on the longest edge, not a file size
},
attachmentKeyword: "defect",
displayFilename: true,
minAttachmentCount: 1,
maxAttachmentCount: 5,
useOriginalFilename: false, // REQUIRED, or filenameExpression never runs
filenameExpression: "$feature.asset_id + '-' + Text(Now(), 'YMMDD')", // Y, not YYYY
visibilityExpression: "hasDefect"
});
const formTemplate = new FormTemplate({
title: "Pole inspection",
description: "Complete every section before submitting",
supportsAttachmentElements: true, // DEFAULTS TO FALSE. Without this, nothing renders.
elements: [photos],
expressionInfos: [{
name: "hasDefect",
expression: "$feature.condition != 'OK'",
returnType: "boolean"
}]
});
const poles = new FeatureLayer({
portalItem: { id: "<your-item-id>" },
outFields: ["*"],
formTemplate
});
Four lines in there are load-bearing, and three of them are corrections to the obvious version.
AttachmentElement reference page shows
maxFileSize: 800 inside a block commented “autocastable to
ImageInput”. ImageInput has no
maxFileSize property — it has
inputMethod, maxImageSize and
type. maxImageSize is a pixel dimension on the
longest edge, and larger images are resized with aspect ratio maintained. The property called
maxFileSize exists only on DocumentInput, and it is
in megabytes. Copy the vendor sample onto an image input and your size cap is silently
dropped — you have capped nothing, and every full-resolution phone photo goes straight into the
storage bill. Verify against the input class you are actually using, not the element page.expressionInfos is what
visibilityExpression refers to by name — the expression lives on the
template, not the element. filenameExpression is the exception to that pattern:
per the docs it should be handled inline on the element rather than as a shared reference.
And watch the date token. Arcade’s Text() format specifiers define
Y for the full year and YY for a two-digit year.
There is no YYYY. Write it anyway and it parses as two two-digit years, so 2026
renders as 2626 — a wrong filename, generated silently, in the one property you added the
expression for. Esri’s own default for this property uses
Y.
Trap one: the template that ignores attachment elements
The first default is the one to check before you debug anything else, because it sits on the class you
are already constructing. FormTemplate.supportsAttachmentElements indicates
whether the form supports attachment elements at all, and it defaults to
false. Leave it alone and any attachment element you configured is simply not
part of the form.
This one is editor-agnostic. It bites the Editor component just as hard as the widget, which matters because the component is otherwise the configuration-free path — and it is easy to read “available by default in the Editor component” in the release notes as meaning there is nothing to set. There is exactly one thing to set, and it is on the template.
Trap two: the filename expression that never runs
filenameExpression is the property most worth having and the easiest to
configure into uselessness. It applies only when useOriginalFilename is
false, or when allowUserRename is
false and the user adds an attachment without naming it. Both properties default
to true.
So the natural configuration — set filenameExpression, leave everything
else alone — keeps the original camera filename and never evaluates your expression. You get exactly
the IMG_4471.jpg you were trying to eliminate, with no indication that anything was ignored.
Trap three: formSystem defaults to legacy
The new elements are rendered by a new form system. The Editor component —
arcgis-editor — uses it by default. The Editor widget
does not, and must be told:
// Editor WIDGET (deprecated at 5.0)
const editor = new Editor({ view, formSystem: "next" }); // constructor form; more robust
// editor.viewModel.formSystem = "next"; // also valid, post-construction
The default is legacy, deliberately, to preserve behavior for applications built before 5.1. Defensible by Esri, and it produces a nasty failure to debug: a form template that validates, an element simply absent from the rendered form, and nothing in the console explaining why.
EditorViewModel.featureFormViewModel or
EditorViewModel.attachmentsViewModel should remain in legacy mode until
they have migrated to the Editor component. Those break when you set
formSystem to next — not later, when you migrate. If you drive your own UI
off either view model, the one-line opt-in is the thing that breaks production, not the migration.Worth knowing that the bridge is itself temporary: formSystem, and support for
the legacy form APIs, are documented for removal at 6.0 when next becomes the default.
And the keyword that hides your existing photos
This is the one most likely to bite a real project, and it is the reason the general
AttachmentInput exists.
When you use a specific typed input — image, audio, video, document — existing attachments are displayed only if they match the element’s keyword. Attachments without a matching keyword are not shown. And when a form supports attachment elements but defines none explicitly, existing feature attachments are not displayed at all.
Picture the retrofit. A utility adds an AttachmentElement with
attachmentKeyword: “defect” to a pole-inspection layer carrying
forty thousand legacy photos. Not one of them has that keyword. Crews open records and see nothing. The data
is intact and entirely invisible. To show attachments that have no corresponding keyword, use the general
AttachmentInput and set its
attachmentAssociationType accordingly. The docs also describe a compatibility
fallback for forms authored under older workflows, where the Editor recognizes existing attachments and
preserves prior behavior — find out which side of that fallback your layer lands on before you ship,
not after.
Multiple choice, at last
MultipleChoiceInput is the smallest of the three additions and the one your
field crews will notice first. Choices are an array of label and
value pairs, with minimumChoices and
maximumChoices for validation, choiceDelimiter
controlling how several selections serialize into one field value, an
includeSelectAllChoices convenience, and an
otherChoice escape hatch. Note that
otherChoice writes to a separate field you name via its
fieldName property — it does not append free text into the delimited
value.
That delimiter deserves a decision rather than a default. It defaults to a comma, selections are concatenated into a single field value, and a comma inside a label will produce a value you cannot parse back apart. Pick a delimiter your labels cannot contain, and pick it before anyone collects data.
What it costs
ATS rule: never assume a feature is free. Attachments are storage, and storage is credits. Per the ArcGIS Online credit documentation, the two rates that matter here are not the same:
| What | Rate | Roughly |
|---|---|---|
| Feature storage | 2.4 credits per 10 MB per month | ~240 credits per GB per month |
| File and attachment storage | 1.2 credits per 1 GB per month | ~1.2 credits per GB per month |
Attachments bill at the file rate — 200 times cheaper per gigabyte than feature storage. Both are calculated hourly. A photo-heavy inspection program is therefore affordable on storage, which is the good news.
maxImageSize and maxAttachmentCount are client-side
form configuration. Anything that calls the service’s addAttachment
endpoint directly — another app, a script, a bulk loader — ignores them completely. Treat them as
how you shape the intended workflow, not as a control on what the service will accept. If spend actually
matters, govern it at the service and audit it, the same way you would treat
minAttachmentCount as a hint rather than a guarantee.Security and permissions, briefly
Two things the release notes will not tell you, and the first is easy to get backwards. Adding or deleting an attachment rides on the same Create or Update capability that governs feature editing — there is no separate per-user attachment permission to grant. The distinction is at the layer: the service must separately advertise attachment support, which is a different flag from whether the layer is editable. So the question to ask of a layer is not “who may attach files” but “does this layer advertise attachments at all” — and it must be editable too, or none of this works.
And the typed inputs are a picker constraint, not a content check. Declaring
type: “image” shapes what the file dialog offers. It is not MIME
sniffing, not malware scanning, and not server-side file-type enforcement. If you are capturing files from
the public rather than from a known crew, that gap is yours to close.
The limitations that decide your design
The AttachmentElement reference lists nine constraints. Four shape the
application:
- Single feature updates only. Attachment element support “is limited to single feature updates.” Bulk edit workflows do not get attachment elements.
- The form template must live in one of two places — on the Editor’s
layerInfo, or directly on the feature layer. A template supplied any other way will not drive attachments. - Input modes that force capture — camera only, no gallery — are unsupported in this release, for the audio, image and video inputs.
- Minimum-count validation is, in practice, unreachable this release. Per the docs it
applies only in mobile editing workflows where the input method allows capture only and the minimum is at
least one — which is precisely the configuration the previous bullet says is unsupported. Read those
two together and the conclusion is blunt: treat
minAttachmentCountas documentation of intent, not as validation, and enforce the count server-side if it actually matters.
Migrating off the widget, honestly
Widgets were deprecated at 5.0, and the docs say they will begin to be removed in Q1 2027 at version 6.0. Any editing app you expect to still be running a year from now is making this move regardless; 5.1 adds a reason to start sooner. But “migrate to the component” is a sentence, not a plan, so here is the honest shape of it.
Widget to web component is not a rename. The packaging differs, the event model differs — DOM
events rather than watch and on handles — and
the component expects a map component rather than a MapView handed to a
constructor. Theming moves toward Calcite custom properties. Budget it as a real piece of work on any app
with custom editing UI.
Two things make it less alarming than it sounds. EditorViewModel itself is not
deprecated, so headless applications that drive their own interface are not being evicted. And the sequencing
is under your control: if you depend on
featureFormViewModel or attachmentsViewModel, stay in
legacy mode and migrate deliberately rather than flipping a flag and finding out.
Where this lands in a real project
This is a meaningful upgrade for anything that captures evidence in the field — utility inspections, code enforcement, damage assessment, asset condition surveys. The pattern that used to require custom UI work — show the photo slot only when there is something to photograph, name the file after the asset, cap it at five, and lock it once the record is approved — is now four properties on a form element, three of them Arcade one-liners.
What it does not change is the data model. Attachments are still attachments, still billed as file storage, still bound to a layer that must have the capability enabled, and still governed at the service rather than at the form. What changed is that the form finally knows they exist — once you have found all three defaults.
References
- Release notes for 5.1 — ArcGIS Maps SDK for JavaScript (AttachmentElement, MultipleChoiceInput, the
supportsAttachmentElementsaddition, the legacy view-model advisory, widget removal beginning at 6.0) - AttachmentElement — API reference (property list, the nine limitations, keyword behavior for existing attachments)
- FormTemplate — API reference (
supportsAttachmentElements, defaultfalse) - ImageInput — API reference (
maxImageSizein pixels on the longest edge; there is nomaxFileSizehere) - DocumentInput — API reference (
maxFileSize, in megabytes) - MultipleChoiceInput — API reference (
choices,choiceDelimiter,otherChoiceand itsfieldName) - EditorViewModel — API reference (
formSystemdefault value and its own deprecation) - Understand credits — ArcGIS Online Help (feature storage and file storage credit rates, calculated hourly)
Running an inspection program on a form nobody wants to fill in?
We design ArcGIS editing apps around the crew doing the work — conditional capture, enforced naming, validation that holds at the service and not just in the browser — and we build them on the component stack rather than the one being retired.
Book a free intro call