Skip to content
Log in

Liquid updates

On this page

The update tag writes custom data fields to Salesforce records directly from a Liquid controller template. Use it to save storefront actions back to your records — tracking product views, storing customer preferences, recording form submissions, or updating order details.

:::warning Controller context required. The update tag works only inside a Liquid controller template (in controllers/<controller>/<action>.liquid). It is a silent no-op outside a controller. :::

When to use update

Use the update tag when you need to: - Track metrics (view counts, interaction history) on storefront records - Save customer preferences from form input - Store order metadata or notes from a storefront action - Record fulfillment or status changes initiated by a customer - Audit or log storefront behavior to custom fields

:::note The update tag writes one field at a time to one existing record. If you need to update multiple records or multiple fields at once, consider a Flow or Apex action instead. :::

When NOT to use update

  • Creating new records — use a Flow or Apex action to insert records. The update tag modifies existing records only.
  • Standard Salesforce fields — use a Flow to update fields like Name, Description, IsActive. The update tag can only write custom data fields.
  • Managed-package fields — these are read-only from Liquid. Use a Flow or Apex.
  • Unscoped writes — if your update logic cannot guarantee the record belongs to the current store and customer, do not use update. The tag is a silent no-op if preconditions fail — the write might not happen, and you will not know.

Syntax

```liquid

{% update , field: "", value: %} ```

Parameters

Parameter Type Required Description
<drop> Drop Yes The object to update. Must be a Drop instance (e.g. current_product, current_order, current_customer). To update a record from {% query %}, cast it first: \| cast: '<DropName>'.
field: string Yes The custom field API name (case-insensitive). Example: view_count__c, favorite__c, last_purchase_date__c.
value: any Yes The new value. Can be a string, number, boolean, null, or Liquid variable.

Preconditions for success

All four of these must be true for the update to succeed:

  1. Inside a controller template — the tag is in a file at controllers/<controller>/<action>.liquid. Outside a controller, the tag is a silent no-op.
  2. Drop object — the first argument is a Drop (a typed object), not a raw record from {% query %}. Convert query results with | cast: '<DropName>' before updating.
  3. Editable custom field — the field must have a Custom Data Mapping marked Read/Write. If the mapping is Read-only, a warning appears in the web console (surfaced by {% debug %}), not the browser console, and the write does not happen.
  4. Custom field only — the field must be a custom data field from a Custom Data Mapping. Standard fields (Name, IsActive, CreatedDate) and managed-package fields cannot be updated.

If any precondition fails, the tag is a silent no-op and logs a warning to the web console (surfaced by {% debug %}), not the browser console. There is no error message in the page output.

Before you start: Custom Data Mapping setup

Before you can update a field, it must be mapped in Custom Data Mapping and marked editable:

  1. Open the relevant Custom Data Mapping (e.g., for Product custom data, open the mapping for your Product object).
  2. Locate the field you want to update.
  3. Ensure the mapping mode is set to Read/Write — Read-only mappings cannot be written to.
  4. Save the mapping.

If the field has no Custom Data Mapping, or the mapping is Read-only, update will not work. Check the web console (surfaced by {% debug %}) for warnings.

Examples

Example 1: Track product view count

Store the number of times a product is viewed in a custom field:

```liquid

{% before %} {% assign current_count = current_product.data.view_count__c | default: 0 | plus: 1 %} {% update current_product, field: “view_count__c”, value: current_count %} {% endbefore %} ```

How it works: 1. Read the current view count (default to 0 if the field is blank). 2. Increment by 1. 3. Write the new count back to the product.

Example 2: Save customer display preference

Store a customer’s selected display mode from a form:

```liquid

{% before %} {% assign display_mode = current_request.params.mode %}

{% if display_mode == “grid” or display_mode == “list” %} {% update current_customer, field: “preferred_display__c”, value: display_mode %} {% endif %} {% endbefore %} ```

How it works: 1. Extract the mode query parameter. 2. Validate that it is one of the allowed values. 3. Write the preference to the customer record.

Example 3: Record a form submission timestamp

Store when a customer last submitted a form:

```liquid

{% before %} {% assign form_name = current_request.params.form %}

{% if form_name != blank %} {% assign now = “now” | date: “%Y-%m-%d %H:%M:%S” %} {% update current_customer, field: “last_form_submission__c”, value: now %} {% endif %} {% endbefore %} ```

How it works: 1. Extract the form name from the request. 2. Generate a timestamp in ISO format. 3. Store the timestamp on the customer record.

Example 4: Update a queried record

Fetch a record via query, cast it, and update a field:

```liquid

{% before %} {% assign order_id = current_request.params.id %}

{% query ‘Order’ as orders, s_c__store_id__c: current_store.sfid, sfid: order_id, billtocontactid: current_customer.sfid %}

{% if orders.size == 1 %} {% assign order = orders[0] | cast: ‘Order’ %} {% update order, field: “storefront_reviewed__c”, value: true %} {% endif %} {% endbefore %} ```

How it works: 1. Query for the order by ID, filtered to the current store and customer. 2. Ensure exactly one match (no unscoped writes). 3. Cast the record to an Order Drop. 4. Update the custom field.

Example 5: Conditional update with validation

Update only if the customer meets certain conditions:

```liquid

{% before %} {% assign feedback = current_request.params.feedback %}

{% if feedback.size > 0 and feedback.size <= 500 %} {% update current_customer, field: “latest_feedback__c”, value: feedback %} {% else %} {% respond body: ‘{“error”: “Feedback must be 1-500 characters”}’, status: 400, layout: false %} {% endif %} {% endbefore %} ```

How it works: 1. Validate the input (length check). 2. Update only if validation passes. 3. Return an error response if validation fails.

Common patterns

Incrementing counters

```liquid

{% assign new_count = current_product.data.count__c | default: 0 | plus: 1 %} {% update current_product, field: “count__c”, value: new_count %} ```

Storing boolean flags

```liquid

{% update current_customer, field: “opted_in__c”, value: true %} ```

Storing a null value (clearing a field)

```liquid

{% update current_product, field: “optional_notes__c”, value: null %} ```

Storing a JSON structure (as a string)

```liquid

{%- new Map preferences -%} {%- assign preferences = preferences | set_key: “theme”, “dark” | set_key: “notifications”, true -%} {%- assign pref_json = preferences | json -%} {% update current_customer, field: “preferences_json__c”, value: pref_json %} ```

Important notes

:::warning Only updates existing records. The update tag modifies fields on records that already exist in Salesforce. It does not create records. If the record does not exist, the tag is a silent no-op. :::

:::warning Scoping is your responsibility. Always verify that the record you are updating belongs to the current store and (for customer data) the authenticated customer. Without explicit scoping, you risk updating records that do not belong to this customer. Example scoping pattern:

```liquid

{% query ‘Product2’ as products, s_c__store_id__c: current_store.sfid, sfid: current_request.params.product_id %}

{% if products.size == 1 %} {% assign product = products[0] | cast: ‘Product’ %} {% update product, field: “custom__c”, value: 100 %} {% endif %} ```

:::

:::note Update happens in the before phase. Place {% update %} in a {% before %} block to ensure the write completes before the response is rendered. Updates in {% final %} run after the response is sent to the client and should be used only for non-blocking, fire-and-forget updates. :::

:::note No transaction rollback. If your controller logic fails after an update, the update is not rolled back. Design your controller flow so that updates happen only when the full operation is safe. :::

:::warning A failed save is currently silent. If the write reaches the record but Salesforce rejects it on save (for example a validation-rule failure), the error is swallowed and written only to the server log; it does not surface in the web console or the page. Confirm the value actually persisted (with {% debug %} or the verify-custom-data-in-liquid walkthrough) rather than assuming success. :::

Drop objects available for update

These global Drops can be passed directly to update:

Drop Description
current_product The current product. Updates go to the Product record.
current_customer The authenticated customer. Updates go to the Contact record.
current_order The current order. Updates go to the Order record.
current_cart The current cart. Updates go to the Cart (s_c__Cart__c) record.
current_store The current store. Updates go to the Store record.
current_account The current account (on Account-scoped pages). Updates go to the Account record.

For records from {% query %}, cast them first:

```liquid

{% assign product = query_result | cast: ‘Product’ %} {% update product, field: “custom__c”, value: new_value %} ```

Debugging updates

If an update does not work, check these in order:

  1. Is this inside a controller template? The tag is a silent no-op outside controllers/<controller>/<action>.liquid.
  2. Is the first argument a Drop? If you pass a raw query result, cast it first with | cast: '<DropName>'.
  3. Does the field have a Custom Data Mapping? Open the mapping and confirm it exists.
  4. Is the mapping marked Read/Write? A Read-only mapping will not allow writes. Change it to Read/Write.
  5. Check the web console. If a precondition fails, a warning is logged there via {% debug %} (not in the browser console or the page output).

To confirm a field is writable, use the verify-custom-data-in-liquid article to test read and write access.

See also

Was this article helpful?

Was this article helpful?