When working with the SharePoint JavaScript Object Model (JSOM), one of the most frequent hurdles developers face is correctly retrieving data from custom fields. While basic properties like Title or ID are straightforward, custom columns often require a specific syntax within the clientContext.load() method to ensure they are properly initialized and accessible.
In this guide, you will learn how to correctly include custom fields in your load requests, whether you are dealing with a single list item or a collection of items. We will also explore the critical importance of internal field names and how to optimize your code to avoid common performance pitfalls.
Understanding the Syntax for Single Items
If you are working with a single object—such as a specific SP.ListItem, SP.Web, or SP.List—the syntax for clientContext.load() is quite flexible. However, it is also where many developers make mistakes by trying to pass multiple fields as a single comma-separated string.
To load multiple custom fields for a single item, you should pass them as individual string arguments following the object reference. For example:
clientContext.load(singleItem, 'Title', 'InternalFieldName', 'AnotherCustomField');
Alternatively, you can pass an array of strings. This is particularly useful if your field list is dynamic or stored in a configuration object:
var fieldsToLoad = ['Title', 'InternalFieldName', 'Status'];
clientContext.load(singleItem, fieldsToLoad);
By explicitly defining these fields, you are telling SharePoint exactly which data to serialize and send over the wire, which keeps your application's memory footprint small.
Loading Custom Fields in Collections
When you are dealing with a collection of items—for instance, when you fetch all items in a list or a subset via a CAML query—the syntax changes. You cannot simply list the fields as arguments. Instead, you must use the Include keyword.
If you try to load a collection without Include, SharePoint will only return the default properties of the collection itself, not the data within the individual items. Here is the correct syntax for collections:
clientContext.load(collOfItems, 'Include(Title, CustomField1, CustomField2)');
This tells the server to iterate through the collection and include the specified fields for every item returned. If you forget this step and try to access a custom field after the executeQueryAsync call, you will likely encounter the dreaded "Property or field has not been initialized" error.
The Internal Name Trap
One of the most common reasons a clientContext.load() call fails or returns undefined values is the use of the "Display Name" instead of the "Internal Name."
SharePoint stores two names for every column: the Display Name (what you see in the UI) and the Internal Name (what the API uses). If you create a column named "Project Status," its internal name might be Project_x0020_Status. If you later rename that column to "Current Status" in the UI, the internal name remains Project_x0020_Status.
How to Find the Correct Internal Name
- Check the URL: Navigate to your List Settings and click on the column name. Look at the URL in your browser; the internal name is at the very end (e.g.,
&Field=InternalName). - Use Browser DevTools: If you have already loaded an item with all its values, you can inspect it in the console to see the real keys:
// Run this in your console after a successful load
console.log(listItem.get_fieldValues());
- SharePoint Designer: Open the list in SharePoint Designer to see a comprehensive view of all field schemas, including their static and internal names.
Optimization: Replacing Loops with Batch Queries
A common pattern that leads to performance issues is looping through an array of IDs and calling getItemById and context.load inside the loop. This can result in a massive request payload or multiple round-trips to the server.
Instead of this:
// Sub-optimal approach
for (var i = 0; i < itemIds.length; i++) {
listItems[i] = list.getItemById(itemIds[i]);
context.load(listItems[i], 'Title', 'CustomField');
}
context.executeQueryAsync(success, fail);
A more efficient approach is to use a CamlQuery to fetch all necessary items in a single collection. This allows you to use the Include syntax and reduces the overhead of managing individual item objects:
var camlQuery = new SP.CamlQuery();
// You can define specific IDs in the ViewXml if needed
camlQuery.set_viewXml("<View><Query><Where><In><FieldRef Name='ID'/><Values><Value Type='Number'>1</Value><Value Type='Number'>2</Value></Values></In></Where></Query></View>");
var items = list.getItems(camlQuery);
clientContext.load(items, "Include(Title, CustomField)");
clientContext.executeQueryAsync(function() {
var enumerator = items.getEnumerator();
while (enumerator.moveNext()) {
var item = enumerator.get_current();
console.log(item.get_item('CustomField'));
}
}, onFail);
Frequently Asked Questions
Why does my request fail when a custom field is empty?
If a field is empty (null), the get_item('FieldName') method will return null. However, the load call itself should not fail unless the internal name is misspelled. Always ensure you are checking for null values in your success callback before performing operations on the data.
Can I load all fields without specifying them?
While you can call clientContext.load(item), this only loads the default scalar properties. It does not load all custom fields. For performance reasons, SharePoint requires you to be explicit about which non-default fields you want to retrieve. There is no "select *" equivalent that includes all custom columns automatically in JSOM.
Does the order of fields in Include() matter?
No, the order in which you list fields inside the Include() statement does not matter. However, ensure there are no spaces between the commas and the field names unless those spaces are actually part of the internal name (which is rare, as spaces are usually encoded).
Wrapping Up
Mastering clientContext.load() is essential for any developer working with SharePoint's legacy JavaScript Object Model. By distinguishing between single item loads and collection loads using Include(), and by always verifying your internal field names, you can build more robust and efficient SharePoint applications. Remember to batch your requests whenever possible to ensure the best possible user experience.