skills/arcgis-tables-forms/SKILL.md
Configure FeatureTable for tabular data display and FormTemplate with input elements for feature editing forms. Use when adding spreadsheet-style data grids, building attribute edit forms, configuring form input types and validation, or surfacing related records in forms.
npx skillsauth add SaschaBrunnerCH/arcgis-maps-sdk-js-ai-context arcgis-tables-formsInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Use this skill for configuring FeatureTable components/widgets and FormTemplate with various input elements.
import FeatureTable from "@arcgis/core/widgets/FeatureTable.js";
import FeatureForm from "@arcgis/core/widgets/FeatureForm.js";
import FormTemplate from "@arcgis/core/form/FormTemplate.js";
import FieldColumnTemplate from "@arcgis/core/widgets/FeatureTable/support/FieldColumnTemplate.js";
const FeatureTable = await $arcgis.import(
"@arcgis/core/widgets/FeatureTable.js",
);
const FeatureForm = await $arcgis.import("@arcgis/core/widgets/FeatureForm.js");
const FormTemplate = await $arcgis.import("@arcgis/core/form/FormTemplate.js");
const FieldColumnTemplate = await $arcgis.import(
"@arcgis/core/widgets/FeatureTable/support/FieldColumnTemplate.js",
);
Note: The examples in this skill use Direct ESM imports. For CDN usage, replace
import X from "path"withconst X = await $arcgis.import("path").
<arcgis-map item-id="YOUR_WEBMAP_ID">
<arcgis-zoom slot="top-left"></arcgis-zoom>
</arcgis-map>
<arcgis-feature-table reference-element="arcgis-map"></arcgis-feature-table>
<script type="module">
const map = document.querySelector("arcgis-map");
const table = document.querySelector("arcgis-feature-table");
const view = await map.view;
await view.when();
const layer = view.map.layers.find((l) => l.type === "feature");
table.layer = layer;
</script>
| Property | Type | Default | Description |
| ----------------------------- | ---------------------- | ------- | -------------------------------- |
| layer | FeatureLayer | | The layer to display |
| attribute-table-template | AttributeTableTemplate | | Table configuration (new in 5.0) |
| editing-enabled | boolean | false | Enable inline editing |
| filter-by-selection-enabled | boolean | false | Filter by selected features |
| highlight-disabled | boolean | false | Disable row highlighting |
| hide-header | boolean | false | Hide the table header |
| hide-menu | boolean | false | Hide the table menu |
| hide-selection-column | boolean | false | Hide the selection column |
| multiple-selection-disabled | boolean | false | Disable multi-row selection |
| multiple-sort-enabled | boolean | false | Enable multi-column sorting |
| page-size | number | | Rows per page |
| auto-save-disabled | boolean | false | Disable auto-saving edits |
| definition-expression | string | | Filter expression |
| time-zone | string | | Time zone for dates |
| Property | Description |
| ----------------------------------------- | -------------------------------- |
| hide-menu-items-clear-selection | Hide clear selection menu item |
| hide-menu-items-delete-selection | Hide delete selection menu item |
| hide-menu-items-export-selection-to-csv | Hide export to CSV menu item |
| hide-menu-items-refresh-data | Hide refresh data menu item |
| hide-menu-items-toggle-columns | Hide toggle columns menu item |
| hide-menu-items-zoom-to-selection | Hide zoom to selection menu item |
| Event | Description |
| ----------------------- | --------------------------------------- |
| arcgisSelectionChange | Fires when row selection changes |
| arcgisCellClick | Fires when a cell is clicked |
| arcgisCellKeydown | Fires on keyboard interaction in a cell |
| arcgisCellPointerover | Fires when pointer enters a cell |
| arcgisCellPointerout | Fires when pointer leaves a cell |
| arcgisColumnReorder | Fires when columns are reordered |
| arcgisPropertyChange | Fires when a property changes |
| arcgisReady | Fires when the component is ready |
<arcgis-feature-table
reference-element="arcgis-map"
editing-enabled
multiple-sort-enabled
page-size="50"
hide-menu-items-delete-selection
></arcgis-feature-table>
<script type="module">
const table = document.querySelector("arcgis-feature-table");
table.layer = featureLayer;
table.addEventListener("arcgisSelectionChange", (event) => {
console.log("Selection changed");
});
</script>
import FeatureTable from "@arcgis/core/widgets/FeatureTable.js";
const featureTable = new FeatureTable({
view: view,
layer: featureLayer,
container: "tableDiv",
});
const featureTable = new FeatureTable({
view: view,
layer: featureLayer,
container: "tableDiv",
editingEnabled: true,
multiSortEnabled: true,
highlightEnabled: true,
attachmentsEnabled: true,
relatedRecordsEnabled: true,
pageSize: 50,
filterGeometry: view.extent,
});
import FieldColumnTemplate from "@arcgis/core/widgets/FeatureTable/support/FieldColumnTemplate.js";
const featureTable = new FeatureTable({
view: view,
layer: featureLayer,
tableTemplate: {
columnTemplates: [
new FieldColumnTemplate({
fieldName: "name",
label: "Name",
initialSortPriority: 0,
direction: "asc",
}),
new FieldColumnTemplate({
fieldName: "status",
label: "Status",
}),
new FieldColumnTemplate({
fieldName: "value",
label: "Value ($)",
textAlign: "right",
}),
],
},
});
| Property | Type | Description |
| --------------------- | ------- | ---------------------- |
| fieldName | string | Field to display |
| label | string | Column header text |
| editable | boolean | Allow editing |
| visible | boolean | Column visibility |
| frozen | boolean | Freeze column to left |
| frozenToEnd | boolean | Freeze column to right |
| resizable | boolean | Allow column resizing |
| autoWidth | boolean | Auto-size column width |
| width | number | Column width in pixels |
| textAlign | string | Text alignment |
| textWrap | boolean | Wrap text in cells |
| initialSortPriority | number | Sort priority |
import GroupColumnTemplate from "@arcgis/core/widgets/FeatureTable/support/GroupColumnTemplate.js";
const featureTable = new FeatureTable({
view: view,
layer: featureLayer,
tableTemplate: {
columnTemplates: [
new GroupColumnTemplate({
label: "Location",
columnTemplates: [
new FieldColumnTemplate({ fieldName: "city", label: "City" }),
new FieldColumnTemplate({ fieldName: "state", label: "State" }),
new FieldColumnTemplate({ fieldName: "country", label: "Country" }),
],
}),
new GroupColumnTemplate({
label: "Details",
columnTemplates: [
new FieldColumnTemplate({ fieldName: "name", label: "Name" }),
new FieldColumnTemplate({ fieldName: "type", label: "Type" }),
],
}),
],
},
});
Configure table display using AttributeTableTemplate, which can be set on a layer or on the feature table component.
import AttributeTableTemplate from "@arcgis/core/tables/AttributeTableTemplate.js";
const tableTemplate = new AttributeTableTemplate({
elements: [
{
type: "field",
fieldName: "name",
label: "Name",
},
{
type: "field",
fieldName: "status",
label: "Status",
},
{
type: "group",
label: "Location",
elements: [
{ type: "field", fieldName: "city", label: "City" },
{ type: "field", fieldName: "state", label: "State" },
],
},
],
orderByFields: [{ field: "name", order: "asc" }],
});
// Set on the feature table component
const table = document.querySelector("arcgis-feature-table");
table.attributeTableTemplate = tableTemplate;
// Or set on the layer
featureLayer.attributeTableTemplate = tableTemplate;
| Type | Class | Description |
| -------------- | --------------------------------- | ----------------------- |
| field | AttributeTableFieldElement | Individual field column |
| group | AttributeTableGroupElement | Group of columns |
| relationship | AttributeTableRelationshipElement | Related records |
| attachment | AttributeTableAttachmentElement | Attachment column |
// Select by ObjectIDs
featureTable.highlightIds.add(123);
featureTable.highlightIds.addMany([124, 125, 126]);
// Clear selection
featureTable.highlightIds.removeAll();
// Select from query
const results = await featureLayer.queryObjectIds({
where: "status = 'active'",
});
featureTable.highlightIds.addMany(results);
featureTable.highlightIds.on("change", (event) => {
console.log("Added IDs:", event.added);
console.log("Removed IDs:", event.removed);
const selectedIds = featureTable.highlightIds.toArray();
console.log("All selected:", selectedIds);
});
// Map click selects in table
view.on("click", async (event) => {
const response = await view.hitTest(event);
const feature = response.results.find(
(r) => r.graphic.layer === featureLayer,
);
if (feature) {
featureTable.highlightIds.removeAll();
featureTable.highlightIds.add(feature.graphic.attributes.OBJECTID);
}
});
// Table selection highlights on map
let highlightHandle;
featureTable.highlightIds.on("change", async () => {
const layerView = await view.whenLayerView(featureLayer);
if (highlightHandle) {
highlightHandle.remove();
}
const objectIds = featureTable.highlightIds.toArray();
if (objectIds.length > 0) {
highlightHandle = layerView.highlight(objectIds);
}
});
// Filter by geometry
featureTable.filterGeometry = view.extent;
// Filter by expression
featureTable.layer.definitionExpression = "category = 'A'";
// Refresh data
featureTable.refresh();
// Clear filters
featureTable.filterGeometry = null;
featureTable.layer.definitionExpression = null;
Configure edit forms for features.
import FormTemplate from "@arcgis/core/form/FormTemplate.js";
const formTemplate = new FormTemplate({
title: "Edit Feature",
description: "Update the feature attributes",
elements: [
{
type: "field",
fieldName: "name",
label: "Name",
},
{
type: "field",
fieldName: "category",
label: "Category",
},
{
type: "field",
fieldName: "description",
label: "Description",
},
],
});
featureLayer.formTemplate = formTemplate;
| Property | Type | Description |
| ------------------------------- | ---------------- | ----------------------------- |
| title | string | Form title |
| description | string | Form description |
| elements | FormElement[] | Array of form elements |
| expressionInfos | ExpressionInfo[] | Arcade expression definitions |
| preserveFieldValuesWhenHidden | boolean | Keep hidden field values |
| Type | Description |
| -------------- | -------------------------------------- |
| field | FieldElement - Edit a specific field |
| group | GroupElement - Group of elements |
| text | TextElement - Static text/HTML content |
| relationship | RelationshipElement - Related records |
| attachment | AttachmentElement - File attachments |
{
type: "field",
fieldName: "name",
label: "Name",
description: "Enter the feature name",
hint: "Required field",
requiredExpression: "always-required",
editableExpression: "$feature.status != 'locked'",
visibilityExpression: "$feature.type != 'hidden'"
}
{
type: "group",
label: "Location",
description: "Address information",
initialState: "collapsed", // expanded, collapsed
elements: [
{ type: "field", fieldName: "address", label: "Address" },
{ type: "field", fieldName: "city", label: "City" },
{ type: "field", fieldName: "state", label: "State" }
]
}
{
type: "text",
text: "<h3>Important Instructions</h3><p>Please fill out all required fields.</p>"
}
{
type: "relationship",
relationshipId: 0,
label: "Related Inspections",
description: "View and manage related inspection records",
displayCount: 5,
orderByFields: [{
field: "inspection_date",
order: "desc"
}],
editableExpression: "true"
}
{
type: "field",
fieldName: "name",
label: "Name",
input: {
type: "text-box",
maxLength: 100,
minLength: 1
}
}
{
type: "field",
fieldName: "description",
label: "Description",
input: {
type: "text-area",
maxLength: 1000,
minLength: 0
}
}
{
type: "field",
fieldName: "category",
label: "Category",
input: {
type: "combo-box",
showNoValueOption: true,
noValueOptionLabel: "Select a category..."
}
}
// Works with coded value domains - values auto-populate from domain
{
type: "field",
fieldName: "priority",
label: "Priority",
input: {
type: "radio-buttons",
showNoValueOption: false
}
}
{
type: "field",
fieldName: "is_active",
label: "Active",
input: {
type: "switch",
offValue: 0,
onValue: 1
}
}
{
type: "field",
fieldName: "start_date",
label: "Start Date",
input: {
type: "date-picker",
min: new Date("2020-01-01"),
max: new Date("2030-12-31"),
includeTime: false
}
}
{
type: "field",
fieldName: "event_datetime",
label: "Event Date/Time",
input: {
type: "datetime-picker",
min: new Date("2020-01-01T00:00:00"),
max: new Date("2030-12-31T23:59:59"),
includeTime: true
}
}
{
type: "field",
fieldName: "event_time",
label: "Event Time",
input: {
type: "time-picker"
}
}
{
type: "field",
fieldName: "barcode",
label: "Barcode",
input: {
type: "barcode-scanner"
}
}
const formTemplate = new FormTemplate({
expressionInfos: [
{
name: "show-commercial-fields",
expression: "$feature.property_type == 'commercial'",
},
{
name: "show-residential-fields",
expression: "$feature.property_type == 'residential'",
},
],
elements: [
{ type: "field", fieldName: "property_type", label: "Property Type" },
{
type: "group",
label: "Commercial Details",
visibilityExpression: "show-commercial-fields",
elements: [
{ type: "field", fieldName: "business_name", label: "Business Name" },
{ type: "field", fieldName: "num_employees", label: "Employees" },
],
},
{
type: "group",
label: "Residential Details",
visibilityExpression: "show-residential-fields",
elements: [
{ type: "field", fieldName: "num_bedrooms", label: "Bedrooms" },
{ type: "field", fieldName: "num_bathrooms", label: "Bathrooms" },
],
},
],
});
// Conditionally required
{
type: "field",
fieldName: "inspection_notes",
label: "Inspection Notes",
requiredExpression: "notes-required"
}
// Conditionally editable
{
type: "field",
fieldName: "approved_by",
label: "Approved By",
editableExpression: "is-pending"
}
import FeatureForm from "@arcgis/core/widgets/FeatureForm.js";
const featureForm = new FeatureForm({
container: "formDiv",
layer: featureLayer,
formTemplate: formTemplate,
});
// Set feature to edit
featureForm.feature = selectedGraphic;
// Listen for submit
featureForm.on("submit", () => {
if (featureForm.feature) {
const updated = featureForm.getValues();
featureLayer.applyEdits({
updateFeatures: [
{
attributes: {
...featureForm.feature.attributes,
...updated,
},
geometry: featureForm.feature.geometry,
},
],
});
}
});
// Handle value changes
featureForm.on("value-change", (event) => {
console.log(`${event.fieldName} changed to ${event.value}`);
});
<!DOCTYPE html>
<html>
<head>
<script src="https://js.arcgis.com/5.0/"></script>
<script
type="module"
src="https://js.arcgis.com/5.0/map-components/"
></script>
<style>
html,
body {
height: 100%;
margin: 0;
}
#container {
display: flex;
flex-direction: column;
height: 100%;
}
arcgis-map {
flex: 1;
}
#tableDiv {
height: 300px;
}
</style>
</head>
<body>
<div id="container">
<arcgis-map
basemap="streets-navigation-vector"
center="-118.805,34.027"
zoom="11"
>
<arcgis-zoom slot="top-left"></arcgis-zoom>
</arcgis-map>
<arcgis-feature-table
reference-element="arcgis-map"
editing-enabled
></arcgis-feature-table>
</div>
<script type="module">
const FeatureLayer = await $arcgis.import(
"@arcgis/core/layers/FeatureLayer.js",
);
const mapElement = document.querySelector("arcgis-map");
const view = await mapElement.view;
await view.when();
const featureLayer = new FeatureLayer({
url: "https://services3.arcgis.com/GVgbJbqm8hXASVYi/arcgis/rest/services/Trailheads/FeatureServer/0",
});
mapElement.map.add(featureLayer);
const table = document.querySelector("arcgis-feature-table");
table.layer = featureLayer;
// Highlight table selection on map
let highlightHandle;
table.addEventListener("arcgisSelectionChange", async () => {
const layerView = await view.whenLayerView(featureLayer);
if (highlightHandle) {
highlightHandle.remove();
}
const objectIds = table.highlightIds?.toArray();
if (objectIds && objectIds.length > 0) {
highlightHandle = layerView.highlight(objectIds);
}
});
</script>
</body>
</html>
<!DOCTYPE html>
<html>
<head>
<script src="https://js.arcgis.com/5.0/"></script>
<script
type="module"
src="https://js.arcgis.com/5.0/map-components/"
></script>
<style>
html,
body {
height: 100%;
margin: 0;
}
</style>
</head>
<body>
<arcgis-map
basemap="streets-navigation-vector"
center="-118.805,34.027"
zoom="13"
>
<arcgis-zoom slot="top-left"></arcgis-zoom>
<arcgis-editor slot="top-right"></arcgis-editor>
</arcgis-map>
<script type="module">
const FeatureLayer = await $arcgis.import(
"@arcgis/core/layers/FeatureLayer.js",
);
const mapElement = document.querySelector("arcgis-map");
const view = await mapElement.view;
await view.when();
const layer = new FeatureLayer({
url: "https://services.arcgis.com/.../FeatureServer/0",
});
mapElement.map.add(layer);
const editor = document.querySelector("arcgis-editor");
editor.layerInfos = [
{
layer: layer,
formTemplate: {
title: "Property Information",
description: "Enter property details",
preserveFieldValuesWhenHidden: true,
expressionInfos: [
{
name: "is-commercial",
expression: "$feature.type == 'commercial'",
},
],
elements: [
{
type: "field",
fieldName: "name",
label: "Property Name",
input: { type: "text-box", maxLength: 100 },
},
{
type: "field",
fieldName: "type",
label: "Property Type",
input: { type: "combo-box" },
},
{
type: "group",
label: "Commercial Details",
visibilityExpression: "is-commercial",
elements: [
{
type: "field",
fieldName: "business_type",
label: "Business Type",
},
{ type: "field", fieldName: "sqft", label: "Square Footage" },
],
},
{
type: "field",
fieldName: "inspection_date",
label: "Last Inspection",
input: { type: "date-picker" },
},
{
type: "field",
fieldName: "notes",
label: "Notes",
input: { type: "text-area", maxLength: 500 },
},
],
},
},
];
</script>
</body>
</html>
feature-table - FeatureTable component usagewidgets-featuretable-editing - Editing with FeatureTablewidgets-featuretable-map - FeatureTable with map integrationwidgets-featuretable-relates - FeatureTable with related recordsediting-groupedfeatureform - Grouped feature form layoutediting-featureform-fieldvisibility - Controlling field visibility in formsField names must match: fieldName must exactly match the layer field name (case-sensitive).
// Layer has field "PropertyName"
{
fieldName: "PropertyName";
} // Correct
{
fieldName: "propertyname";
} // Wrong - case sensitive
Coded value domains: ComboBox automatically populates from domain values.
// If field has coded value domain, values come from domain
{ type: "field", fieldName: "status", input: { type: "combo-box" } }
// Dropdown shows domain values automatically - no manual options needed
Expression names: Reference expressions by name string, not expression text.
expressionInfos: [{ name: "my-expr", expression: "..." }],
elements: [{
visibilityExpression: "my-expr" // String reference to name
}]
Layer must be editable: Check capabilities before enabling editing.
await layer.load();
if (layer.capabilities?.editing?.supportsUpdateByOthers) {
featureTable.editingEnabled = true;
}
Container size: FeatureTable widget needs explicit height on its container.
#tableDiv {
height: 400px; /* Required - table won't render without explicit height */
width: 100%;
}
Highlight ID type: highlightIds expects ObjectID numbers, not strings.
featureTable.highlightIds.add(123); // Correct - number
featureTable.highlightIds.add("123"); // Wrong - string
arcgis-editing for Editor configuration, applyEdits, and versioning.arcgis-popup-templates for PopupTemplate configuration.arcgis-interaction for hit testing and map selection.arcgis-layers for FeatureLayer configuration.development
Build map user interfaces with ArcGIS widgets, Map Components, and Calcite Design System. Use for adding legends, layer lists, search, tables, time sliders, and custom UI layouts.
development
Add specialized widgets including BuildingExplorer, FloorFilter, Track, Locate, HistogramRangeSlider, ScaleBar, Compass, NavigationToggle, and media viewers. Use for building exploration, indoor mapping, GPS tracking, and data histograms.
data-ai
Style and render geographic data with renderers, symbols, and visual variables. Use for creating thematic maps, heatmaps, class breaks, unique values, labels, and 3D visualization.
tools
Work with ArcGIS Utility Networks for modeling and analyzing connected infrastructure including network tracing, associations visualization, and topology validation. Use for electric, gas, water, and telecom network analysis.