---
title: "POS views"
source: https://support.storeconnect.com/articles/pos-views
type: article
format: markdown
site: StoreConnect Support — product and developer documentation for StoreConnect
site_index: https://storeconnect.com/llms.txt
docs_index: https://support.storeconnect.com/llms.txt
note: Append .md to any page or article URL on this site to get its Markdown form.
---
# POS views

A **POS View** is a single HTML template that takes over part of a POS screen. It can contain
HTML, CSS, JavaScript, and Liquid, so it's the option to reach for when action groups and
layouts can't produce the screen you need.

Each view is one record with two fields that matter: an **Identifier** you refer to it by, and
the **HTML Template** itself.

## Where you can use a view

A view is either attached to a record through that record's **POS View** lookup field, or named
by an action's parameters. What you attach it to decides how much of the screen it replaces and
which Liquid variables it receives.

| Attached to | What it replaces | What you get in Liquid |
| --- | --- | --- |
| **POS Layout** (`list` or `grid`) | The records list. The search bar, filters, filter chips, and action buttons still render around it | `records`, the record drops for the current page |
| **POS Layout** (`record`) | The headline and field list inside the record card. The back button and actions still render | `record` |
| **POS Layout Field** | How that one field renders, on a card or in a labeled row | `record` |
| **POS Action Item** | The contents of the button, not what tapping it does | The item's params, plus `action_item`, plus `product` on a `cart:add_product` item |

A layout view renders once for the whole screen, so on a `grid` layout your template draws the
entire grid rather than one card. To render markup per record, attach the view to a **POS
Layout Field** instead.

On a form layout, such as the customer form, a field view has to include a form element
(`<input>`, `<select>`, `<textarea>`) whose `name` attribute matches the field name. Without it
the value is not captured when the form is submitted.

Two actions take a view by identifier rather than through a lookup field:

-   **`modal:open:view`** opens a view as a modal. Pass the identifier as the `view_identifier`
    param. Any other params become Liquid variables, alongside the context carried over from an
    earlier action in the chain.
-   **`nav:view`** opens a view as a full-page screen. Pass `view` (the identifier) or
    `view_sc_id`. Supply `record_id` and `object_name` together to get a `record` variable.

The order confirmation screen and the quick search panel are layouts too, so you customize
them by attaching a view to those layouts rather than through any separate mechanism. The
order confirmation view also gets an `outcome_message` variable alongside `record`.

Layout views and view pages don't scroll on their own, so wrap your content in an overflow
container.

### Reserved identifiers

One identifier changes behavior by name alone. A view saved with the **Identifier**
`product_action_card` replaces the default card for every `cart:add_product` action item that
has no view of its own, and receives the item's params, `action_item`, and `product`.

## Create a view

:::tip
If you are not familiar with using frontend code, you can ask an AI agent to do this work for you, and request design changes in plain language. To use this method for creating views, see [build in POS using AI agents](pos-with-ai-agents).
:::

The following describes how to manually create a POS view record and attach it to one of the records in the table above.

### Before you start

- You need Salesforce admin access to create **POS View** records, and to edit the **POS
  Layout**, **POS Layout Field**, or **POS Action Item** record you will attach it to.
- Decide where the view will be used. That choice sets which Liquid variables your template
  receives, so it's worth settling before you write any markup.

1.  Go to the **POS Views** list and select **New**.
2.  Enter a **POS View Name**. This is the internal name administrators see in Salesforce, not
    anything staff see on a register.
3.  Enter an **Identifier** if you will refer to the view by name: from another view with
    `{% render %}`, from a `modal:open:view` or `nav:view` action, or to claim a reserved identifier
    such as `product_action_card`. A view attached only through a lookup field doesn't need one.
4.  Enter the **HTML Template**. The record won't save while this is empty, and the field holds
    up to 131,072 characters.
5.  Save the record.
6.  Open the **POS Layout**, **POS Layout Field**, or **POS Action Item** record the view
    belongs to, and set its **POS View** lookup field. Skip this step for a view you open by
    identifier from a `modal:open:view` or `nav:view` action.
7.  On a register, reload the POS.

:::warning
Salesforce does not enforce uniqueness on **Identifier**. If two views share one, the POS takes
whichever it finds first, and which that is isn't predictable. Search the **POS Views** list for
an identifier before reusing it.
:::

The screen you attached the view to now renders your template in place of its default. If it
still shows the default, the register hasn't picked up the new record yet. Sync the device from
**Settings** then **Manage data**, described in [Manage POS data](manage-pos-data-sync).

## Update a view

Use this process to change a view that is already attached. Editing the **HTML Template** takes
effect wherever the view is used, so a shared view changes every screen that renders it.

1.  Go to the **POS Views** list and open the view you want to change. The list shows
    **Identifier** alongside the name, and you can search on either.
2.  Edit the **HTML Template**.
3.  Save the record.
4.  On a register, reload the POS.

The register now renders your edited template.

To see what a view is attached to before you change it, open the view and check its **POS Layout
Fields** and **POS Action Items** related lists. Layouts that use the view are not among them,
so check those by filtering the **POS Layouts** list on its **POS View** field.

For a worked example that builds a view and wires it to a layout field from scratch, see
[Add custom fields to the End Shift form](end-shift-form-layout).

## Liquid on a register

The POS runs LiquidJS in the register's browser, not the Liquid engine your storefront theme
uses. The syntax is the same and standard filters like `upcase` and `date` work, but the
StoreConnect-specific set is much smaller.

### Globals that work

These twelve globals are available in any view:

| Global | What it gives you |
| --- | --- |
| `current_store` | The store |
| `current_outlet` | The outlet the register belongs to |
| `current_register` | The register |
| `current_staff` | The signed-in POS user |
| `current_cart` | The cart open on the register |
| `current_customer` | The customer on the current cart |
| `current_account` | The account on the current cart |
| `current_order` | The current order |
| `current_product` | The product in context |
| `current_pricebook` | The price book in use |
| `all_products` | The purchasable products synced to this device |
| `store_variables` | Declared, but currently raises. See the note below |

### Globals that raise an error

Thirty storefront globals are declared in the POS but not implemented, and calling one throws
rather than rendering empty. The ones most likely to be reached for out of habit are
`current_request`, `current_page`, `current_search`, `current_product_category`,
`theme_variables`, `session_variables`, `all_pages`, `all_menus`, `all_media`, and
`all_content_blocks`.

If a view renders blank or the screen errors, an unimplemented global is the first thing to
check.

:::warning
`store_variables` is declared as a global but does not work in a POS view. Reading it raises, and a
view whose template raises renders as an empty box with no error on screen. Put the values your
template needs in `{% assign %}` statements at the top of it instead. Store variables that configure
POS behavior, such as the `pos.payment_options` keys, are unaffected: the app reads those directly
rather than through Liquid.
:::

### Extra filters and tags

On top of the standard LiquidJS set, the POS registers these:

-   **`money`** formats a number as currency.
-   **`keys`, `merge`, `set_key`, `unset_key`, `collect_keys`, `rename_keys`** work on maps,
    and mirror the map filters on the web storefront.
-   **`serialize` and `deserialize`** convert between a value and a string, which is how you
    pass structured data into a `data-` attribute for your JavaScript to read.
-   **`{% query %}`** reads records from the device's local database.
-   **`{% new %}`** creates a `Map` or a `List` in the template, optionally seeded from JSON:
    `{% new Map totals %}` or `{% new List codes = '["a", "b"]' %}`.

Storefront filters that aren't in that list are not available on a register.

## Reusing a view inside another view

Views resolve each other by **Identifier**, so a shared piece of markup can live in its own
view and be pulled into others. A rendered view gets its own scope, so pass in anything it
needs by name:


```liquid

{% render 'product_card_body', product: record, show_price: true %}
```


Globals still reach the rendered view, but the calling template's own variables do not.
There's no folder structure, so use the identifier exactly as it appears on the record.

## JavaScript in a view

Script tags in a view do run, but not the way they would in an ordinary page. The POS renders
your template, then finds each `<script>` and executes it. Inline code and `src` scripts both
work, and three consequences are worth knowing:

-   **Scripts run after the view is on the page.** `DOMContentLoaded` has already fired, so
    don't wait for it. Your elements exist by the time your code runs.
-   **Scripts run in global scope.** They're not scoped to the view.
-   **Scripts run again when the view re-renders.** A top-level `const` or `let` throws on the
    second run because the name is already declared. Wrap your code in an immediately invoked
    function, or hang what you need off `window`.

```html

<script>
  (function () {
    const total = document.querySelector('#my-total')
    // your code here
  })()
</script>
```

The exception is a field view on a `list` or `grid` layout. Those templates are rendered in one
batch for every visible record and injected as HTML, and injected HTML never runs its scripts.
To get code onto a list or grid screen, put the script in a view attached to the layout itself
rather than to one of its fields.

Your JavaScript can drive the POS through the `scAction` function, which calls any built-in
action such as adding to the cart, starting checkout, printing a receipt, or saving a record.
See [scAction](pos-sc-action) for the syntax and the
[POS actions reference](pos-actions-reference) for every action and its parameters.

To call something outside Salesforce from inside a view, see
[Bring external data into POS](bring-external-data-into-pos).

## What a view can't do

-   **It can't reach data that hasn't synced.** A view runs against the device's local
    database, so it sees what the POS has already downloaded, not whatever is in Salesforce
    right now.
-   **It can't run Apex.** Server-side logic has to be reached over HTTP like any other
    external call.
-   **It can't be scoped to one store.** Like every POS configuration record, a view is
    org-wide, so two stores in the same Salesforce org share it. Branch inside the template
    on `current_store` or `current_outlet` where behavior needs to differ.

If a view renders blank after a reload, open the browser console on the register before changing
anything else. An unimplemented global or a Liquid syntax error reports there, and names the
line it failed on.

---

## Follow StoreConnect

- [Email Newsletter](https://storeconnect.com/c/lp-newsletter)
- [LinkedIn Newsletter](https://www.linkedin.com/build-relation/newsletter-follow?entityUrn=7444956928444862464)
- [YouTube](https://www.youtube.com/channel/UCngKdP2x8l1wcbAKW3tvU8g)
- [LinkedIn](https://www.linkedin.com/company/storeconnect)
- [X / Twitter](https://x.com/storeconnecthq)

## Popular Links

- [Partners](https://storeconnect.com/partners)
- [Become a Partner](https://storeconnect.com/become-a-partner)
- [News](https://storeconnect.com/articles/news)
- [Events](https://storeconnect.com/articles/events)
- [Live Events](https://storeconnect.com/live-events)
- [Feature Comparison](https://storeconnect.com/how-we-compare)
- [Download a free trial](https://appexchange.salesforce.com/appxListingDetail?listingId=a0N3A00000FMkeKUAT)
- [Book a Demo](https://storeconnect.com/contact)

## Documentation

- [Help documentation](https://support.storeconnect.com/help-documentation)
- [AI agents](https://support.storeconnect.com/ai)
- [Videos & tutorials](https://support.storeconnect.com/videos-tutorials)
- [Developer reference](https://support.storeconnect.com/developer-reference)
- [Release notes](https://support.storeconnect.com/release-notes)
- [Troubleshooting](https://support.storeconnect.com/troubleshooting)
- [Trust Center](https://trust.getstoreconnect.com/)
- [Status Page](https://status.storeconnect.com/)

## Contact

- info@getstoreconnect.com
- US +1 415 745 3230
- AUS +61 2 8365 2308

100 S Ashley Dr, Suite 600-2461
Tampa FL 33602-600 USA

Level 22, Sydney Place
180 George Street
Sydney, NSW, 2000, AUS

## Machine-readable

- [Site index for agents](https://storeconnect.com/llms.txt): curated map of the StoreConnect site in llms.txt format
- [Documentation index for agents](https://support.storeconnect.com/llms.txt): full technical and product documentation map

Every page and article on this site has a Markdown rendering: append `.md` to its URL.

Continue in Markdown: [Help documentation](https://support.storeconnect.com/help-documentation.md) · [Developer reference](https://support.storeconnect.com/developer-reference.md) · [Videos & tutorials](https://support.storeconnect.com/videos-tutorials.md) · [Release notes](https://support.storeconnect.com/release-notes.md)

---

StoreConnect Support — https://support.storeconnect.com/articles/pos-views