Skip to content
Log in

Liquid controllers guide

On this page

Liquid controllers add server-side logic to your StoreConnect theme pages. They live in the controllers/ directory of your theme and run in three ordered phases around the normal page request. This article explains those phases and the action tags you use inside them.

For a list of all controller routes (HTTP verbs and URL paths), see Liquid controllers.

How controllers work

When a page is requested, StoreConnect matches the URL to a controller and action. The system runs the corresponding controller template in three phases:

  1. {% before %} — runs before the page renders. Use for data preparation, input validation, cart operations, and early redirects.
  2. {% after %} — runs after the main action. Use for redirects based on results, custom JSON responses, and post-processing.
  3. {% final %} — runs after the HTTP response is sent to the browser. Use for fire-and-forget work like analytics calls and logging. Any {% api %} call inside {% final %} is automatically asynchronous.

Not every controller needs all three phases — include only the ones you need.

```liquid

{% before %} {% params product_id: current_request.params.id %} {% variables page_title: “Product detail” %} {% endbefore %}

{% after %} {% if some_condition %} {% redirect to: “/cart” %} {% endif %} {% endafter %}

{% final %} {%- new Map payload -%} {%- assign payload = payload | set_key: “event”, “page_view” -%} {% api url: “https://analytics.example.com/track”, method: “post”, data: payload %} {% endapi %} {% endfinal %} ```

Action tags

Action tags are only valid inside controller phase blocks. Using them outside a phase block has no effect.

{% params %}

Reads values from the incoming request and makes them available as controller parameters for the current action.

```liquid

{% before %} {% params product_id: current_request.params.product_id, quantity: current_request.params.qty | default: 1 %} {% endbefore %} ```

{% variables %}

Sets template variables that are accessible in the page template.

```liquid

{% before %} {% variables page_title: “Featured products”, show_sidebar: true, max_items: 12 %} {% endbefore %} ```

{% redirect %}

Redirects to a different URL and stops further controller execution. All subsequent phases are skipped.

```liquid

{% before %} {% unless current_customer %} {% redirect to: “/auth/sign_in”, alert: “Please log in to continue” %} {% endunless %} {% endbefore %}

{% after %} {% redirect to: “/cart”, notice: “Item added to your cart” %} {% endafter %} ```

Option Description
to URL path to redirect to (required)
notice Flash notice message displayed after redirect
alert Flash alert message displayed after redirect
status HTTP status code (default: 302)

{% respond %}

Sends a custom HTTP response and stops normal page rendering. Used for AJAX endpoints and API-style responses.

```liquid

{% after %} {%- new Map result -%} {%- assign result = result | set_key: “success”, true | set_key: “count”, current_cart.item_count -%} {%- assign body = result | json -%} {% respond status: 200, body: body %} {% endafter %} ```

Option Description
status HTTP status code (required)
body Response body as a string
json Response body as a JSON object (alternative to body)

{% update %}

Updates a custom data field on a database object. The field name uses the platform’s internal identifier format.

```liquid

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

{% action %}

Executes a named platform action. Actions cover cart management, shipping, pricebooks, and promotions.

Cart actions

Action Parameters Description
cart.add product_identifier, quantity, variant_id Add a product to the cart
cart.update cart_item_id, quantity Update an item’s quantity
cart.remove cart_item_id Remove an item from the cart
cart.empty (none) Clear all items from the cart
cart.select identifier Switch to a specific cart
cart.clone cart_id Clone an existing cart

Shipping actions

Action Parameters Description
shipping.set address_id, method Set the shipping address and method

Pricebook actions

Action Parameters Description
pricebook.set pricebook_id Activate a specific pricebook
pricebook.clear (none) Clear the active pricebook

Promotion actions

Action Parameters Description
promotion.apply code Apply a promotion code
promotion.remove code Remove a promotion code
promotion.clear (none) Remove all promotions

Common patterns

Add to cart

```liquid

{% before %} {% action “cart.add”, product_identifier: current_request.params.product_id, quantity: current_request.params.quantity | default: 1, variant_id: current_request.params.variant_id %} {% endbefore %}

{% after %} {% redirect to: “/cart”, notice: “Added to cart” %} {% endafter %} ```

Apply a promotion code

```liquid

{% before %} {% action “promotion.apply”, code: current_request.params.promo_code %} {% endbefore %}

{% after %} {% redirect to: “/cart” %} {% endafter %} ```

Return JSON from a controller

```liquid

{% after %} {%- new Map response -%} {%- assign response = response | set_key: “cart_count”, current_cart.item_count -%} {%- assign json_body = response | json -%} {% respond status: 200, body: json_body %} {% endafter %} ```

Async analytics in {% final %}

API calls inside {% final %} are always asynchronous — the response is sent to the browser before the call completes, so no response data is available.

```liquid

{% final %} {%- new Map event -%} {%- assign event = event | set_key: “type”, “add_to_cart” | set_key: “product”, current_product.id -%} {% api url: “https://analytics.example.com/events”, method: “post”, data: event %} {% endapi %} {% endfinal %} ```

Controller helpers

The helpers/ directory contains shared Liquid logic for controllers. Include a helper with {% render %} inside a phase block:

```liquid

{% before %} {% render “helpers/delivery_options” %} {% endbefore %} ```

Helpers work like snippets but are intended for controller-level logic rather than HTML output.

Was this article helpful?

Was this article helpful?