Articles on: React Flow

React Flow recipes: Liquid in emails, HTTP request bodies, tags from metafields and win-back

React Flow recipes: Liquid in emails, HTTP request bodies, tags from metafields and win-back


In this article:


These recipes show how to combine React Flow steps and Liquid variables to solve common tasks. Each recipe lists the trigger, the steps and the exact values to enter, so you can rebuild it in a few minutes and adjust it to your store.


Before you start: test Liquid with Log output


Almost every text field in React Flow accepts Liquid: email subjects and bodies, tags, HTTP request bodies, file names and more. Variables use the Shopify field names, for example {{ ``order.name`` }}, {{ product.title }} or {{ customer.firstName }}. Click Add variables next to a field to find the right name.


To check what a piece of Liquid returns before you use it:


  1. Add the Log output action ("Log and inspect Liquid variable values for debugging").
  2. Paste your Liquid into Output.
  3. Click Run Test (or Run flow in a One-Shot workflow) and open Log output in the result to see the rendered text.



React Flow uses LiquidJS, not Shopify's native Liquid implementation.

As a result, some Shopify-specific Liquid tags and filters may not be fully supported or may behave differently.

Recipe 1: Liquid in an email template


Goal: a personal order email that greets the customer, lists the purchased products with links, and shows a special line for large orders.


  1. Trigger: Order created (or Order fulfilled for a review request).
  2. Action: Send email.
  3. Send to: {{ ``order.email`` }}
  4. Subject: Thank you for your order {{ ``order.name`` }}, {{ order.customer.firstName | default: "friend" }}!
  5. Message: click <> in the editor toolbar to switch to HTML and paste:


<p>Hi {{ order.customer.firstName | default: "there" }},</p>
<p>Here is what you ordered:</p>
<ul>
{% for line_item in order.lineItems %}
<li><a href="{{ shop.primaryDomain.url }}/products/{{ line_item.product.handle }}">{{ line_item.name }}</a></li>
{% endfor %}
</ul>
{% assign total = order.totalPriceSet.shopMoney.amount | plus: 0 %}
{% if total > 100 %}
<p>Your order qualifies for free gift wrapping. Just reply to this email!</p>
{% endif %}
<p>{{ shop.name }}</p>


  1. Click Preview above the editor to check the layout, then Run Test > Simulate with a real order.


Tips:


  • Use | default: "..." for values that can be empty, such as the first name of a guest customer.
  • For a reliable delivery, send emails through your own SMTP or Microsoft connector.


Recipe 2: JSON body for an HTTP request


Goal: send product data to an external system (an ERP, a warehouse or a webhook) whenever a product is created.


  1. Trigger: Product added to store.
  2. Action: Send HTTP request.
  3. Method: POST
  4. Url: the address of the receiving system, for example https://example.com/webhooks/products
  5. Headers: Content-Type = application/json. Add an authorization header if the system requires one, for example Authorization = Bearer <your token>.
  6. Body:


{
"id": "{{ product.id }}",
"title": {{ product.title | json }},
"tags": {{ product.tags | json }}
}



Why | json? It wraps text in quotes and escapes special characters, and it turns lists into JSON arrays. In our test, {{ product.title | json }} returned "Snowboard: Liquid" and {{ product.tags | json }} returned ["Accessory","customize","Sport","Winter"]. Without the filter, a title with a quote mark would break the JSON.


Optional settings:


  • Handle HTTP error codes: treat error responses as a normal result and get access to the error in the workflow variables, so you can branch on it with a condition.
  • Output results to use in other action or condition: save the response. The field below shows the variable to use in the next steps, for example {{workflow.e021529... | json}}.


Recipe 3: tag products from a metafield


Goal: add a tag based on a product metafield, for example material-cotton from the metafield custom.material, so you can use the tag in collections and filters.


  1. Trigger: Product added to store, or Product updated with a field to observe.
  2. Condition (optional, to skip products without the metafield): Liquid > Liquid result, with {{ product.metafields.custom.material }} and the matcher is not empty.
  3. Action under then: Add product tags with the tag:


material-{{ product.metafields.custom.material | downcase | replace: " ", "-" }}


Replace custom.material with the namespace and key of your metafield. You can find them in Shopify Settings > Metafields and metaobjects > Products: open the metafield definition and look at its namespace and key, for example custom.material.


In our test, a product with the metafield test_data.binding_mount = met3 got the tag binding-met3. Products without the metafield returned an empty value, which is why the condition in step 2 is useful.


To tag the products you already have, build the same steps in a One-Shot workflow: Fetch resources > Products, then the condition and Add product tags under for each.


To add several tags at once, return them separated by commas, for example with a for loop and {% if forloop.last == false %},{% endif %}.


Recipe 4: win-back campaign for inactive customers


Goal: every day, find customers who haven't ordered for 90 days, send each of them a personal discount code once, and mark them so they don't get it again.



  1. Trigger: Scheduled time. Set the start time and click Add repeat: every 1 Days.
  2. Action: Fetch customers.
    • Query type: Segments, Advanced, and in Edit query:

number_of_orders >= 1 AND last_order_date < -90d AND customer_tags NOT CONTAINS 'winback-sent'

  • Optionally add AND email_subscription_status = 'SUBSCRIBED' to email only subscribers.
  • Check the Runs counter: each fetched customer counts as a run.
  1. Under for each: Create discount code.
    • Discount type: Amount off order, Generate random discount code, Percentage 15.
    • Usage limit 1. Customer: {{ ``loop.customer.id`` }}, so only this customer can use the code.
    • Ends in: Within days 14.
    • Select Output results to use in other action or condition. Copy the variable shown under "Use this code to access as variable", for example {{loop.workflow.975ce...}}.



  1. Next: Send email.
    • Send to: {{ loop.customer.defaultEmailAddress.emailAddress }}
    • Subject: We miss you, {{ loop.customer.firstName }}! Here is 15% off
    • In the message, insert the code with the copied variable plus .title, for example {{loop.workflow.975ce....title}}. .discountValue returns the discount value.
  2. Next: Add customer tags with winback-sent. Because the query excludes this tag, each customer gets the offer only once.
  3. Click Save, test with Run Test > Simulate, then Turn on.


There is also a ready-made win-back template in Templates and Playbooks. It runs monthly, picks customers whose last order was 30–365 days ago, and calculates a bigger discount for customers who have been inactive longer. The Playbooks version also provides additional analytics on the revenue generated by the playbook.


Liquid cheat sheet


  • Default value: {{ order.customer.firstName | default: "there" }}
  • Upper or lower case: {{ product.title | upcase }}, {{ product.vendor | downcase }}
  • Replace spaces: {{ product.title | downcase | replace: " ", "-" }}
  • Format a date: {{ order.createdAt | date: "%Y-%m-%d" }}. Use "now" to get the current date, for example: {{ "now" | date: "%Y-%m-%d" }}.
  • Join a list: {{ product.tags | join: ", " }}
  • Safe JSON value: {{ product.title | json }}
  • Condition with a number: {% assign total = order.totalPriceSet.shopMoney.amount | plus: 0 %}{% if total > 100 %}...{% endif %} (plus: 0 turns the amount into a number)
  • Loop: {% for line_item in order.lineItems %}{{ line_item.name }}{% endfor %}
  • Metafield: {{ product.metafields.namespace.key }}


✔️ Next step


Check our articles to learn more about the app's functionality:


Updated on: 14/10/2026

Was this article helpful?

Share your feedback

Cancel

Thank you!