Skip to content
Log in

Final - Liquid Tag Reference

On this page

The final block tag runs its content after the response has been sent to the client. Use it in a Liquid controller for fire-and-forget work like analytics tracking, logging, webhook notifications, and any cleanup that does not affect the customer’s response.

:::note This tag works only inside a Liquid controller template (at controllers/<controller>/<action>.liquid). It is a silent no-op outside a controller. :::

When final runs

In a Liquid controller, execution flows through three phases:

  1. {% before %} block — runs first
  2. Standard action — the main controller logic
  3. Page rendering — the template output
  4. {% after %} block — runs after rendering
  5. {% final %} block — runs after response sent ← you are here

The response has already been sent to the browser, so {% respond %} and {% redirect %} are no-ops in this phase.

Syntax

```liquid

{% final %} {% action “analytics.track”, event: “checkout_complete”, value: current_cart.total %} {% endfinal %} ```

Property Value
Tag Name final
Type Block tag
Source StoreConnect

What’s available in final

  • Response already sent — the client has received their response
  • current_request.params — still accessible
  • All globals — current_store, current_customer, current_cart, etc.
  • Limited tags — {% action %} works, but NOT {% respond %} or {% redirect %} (client already has response)

Common tasks in final

1. Track analytics after purchase

```liquid

{% before %} {% action “cart.checkout” %} {% endbefore %}

{% after %} {% redirect to: current_store.thank_you_path %} {% endafter %}

{% final %} {% action “analytics.track”, event: “purchase_complete”, order_id: current_cart.order_id, total: current_cart.total %} {% endfinal %} ```

2. Send webhook notification asynchronously

```liquid

{% final %} {%- new Map payload -%} {%- assign payload = payload | set_key: “customer_id”, current_customer.id -%} {%- assign payload = payload | set_key: “event”, “login_successful” -%} {%- assign payload = payload | set_key: “timestamp”, “now” | date: “%Y-%m-%dT%H:%M:%SZ” -%} {% endfinal %} ```

3. Perform cleanup or logging

```liquid

{% before %} {% params session_id: current_request.params.session -%} {% endbefore %}

{% final %} {% action “logging.record”, session_id: current_request.params.session_id, status: “completed”, duration: “5000ms” %} {% endfinal %} ```

Key behaviors

  • Runs after response sent — the client has already received their response; you cannot change it
  • {% respond %} is a no-op — the response has been sent; attempting to respond does nothing
  • {% redirect %} is a no-op — the client already has their response; redirects cannot be applied
  • Fire-and-forget operations — use this phase for analytics, logging, webhooks, and cleanup
  • API is forced async — all actions in final are run asynchronously; do not rely on synchronous completion

Why use final

The final block is useful for:

  • Analytics and tracking — record user actions without affecting response time
  • Webhooks — notify external systems asynchronously
  • Logging and audit trails — record events for compliance or debugging
  • Cleanup operations — reset session state, clear temporary data
  • Background tasks — send emails, update counts, sync data

Because final runs after the response is sent, long-running operations here do not delay the customer’s experience.

  • {% before %} — runs before rendering; use for validation and actions
  • {% after %} — runs after page rendering; use for responses or redirects
  • See Liquid controller lifecycle for the complete flow

Was this article helpful?

Was this article helpful?