Zid Docs
AppsThemes
Payments
AppsThemes
Payments
Help Center
Slack
  1. Legacy Theme Migration
  • Getting Started
    • Introduction
    • Theme Development
    • Vitrin Changelog
    • Creating and Managing Theme Presets
    • Legacy Theme Migration
      • Store Settings Mapping
      • Moving to Vitrin Using LLMs
      • Twig to Jinja
      • Breaking Changes
  • Key Concepts
    • Architecture
    • Templates
      • Overview
      • Overridable Templates
      • Legacy Templates
      • Template Replacements
      • Templates Library
        • home.jinja
        • product.jinja
        • cart.jinja
        • category.jinja
        • products.jinja
        • categories.jinja
        • page.jinja
        • blogs.jinja
        • blog.jinja
        • faqs.jinja
        • reviews.jinja
        • questions.jinja
        • shipping_payment.jinja
        • 404_not_found.jinja
    • Settings
      • Schema files
      • Input Settings
      • Media Settings
      • Form Controls Settings
      • Products Settings
      • Additional Settings
      • Conditional Visibility
      • Migrating twig settings schema
    • Localization
      • localization (jinja v. twig)
    • Theme Editor
      • Overview
  • Building with Vitrin
    • Jinja Basics
    • Vitrin's Jinja Extensions
  • Vitrin CLI
    • Introduction
    • CLI Commands
  • Tips & Tricks
    • Performance
  • JS Integration
    • Supporting both Vitrin and Legacy themes
    • Responses & Errors
    • Cart
    • Products
    • Categories
    • Store
    • Account
    • Blogs
    • Options
    • Events
  • Features
    • SDK Popups – Integration Guidelines
    • Custom Styles Guide
    • Gift Card as a Popup
    • Addresses as a Popup
    • Login as a Popup
    • Checkout as a Popup
    • Apple Pay Quick Checkout
    • Region & Language Popup
    • Dynamic Bundle Products
    • Progressive Discounts
    • Customer Wallet & Cashback
    • Add Preorder Support to Your Theme
  • Mobile Apps
    • Scripts
  • API's
    • Authentication
      • Logout
      • Login Status
      • SMS Login
      • Verify SMS Login
      • WhatsApp Login
      • Verify WhatsApp Login
      • Email Login
      • Verify Email Login
      • Register
      • Register Guest
    • Products
      • List Products
      • Search Products
      • Calculate Product Options Price
      • Notify Product Stock Availability
      • Fetch Bundle Offers
      • Fetch Bundle Offers for a Product
      • List My Product Reviews
      • List Product Reviews
      • Create Product Review
      • Update Product Review
      • Delete Product Review
      • List Product Questions
      • Create Product Question
      • Get Product by Slug
      • Get Selection Groups
    • Categories
      • List Categories
    • Checkout
      • Get Cart
      • Remove Cart
      • Duplicate Cart
      • Add Cart Item
      • Empty Cart
      • Update Cart Item
      • Remove Cart Item
      • Upload Cart Input Field
      • Add Gift Card
      • Remove Gift Card
      • Apply Coupon
      • Remove Coupon From Cart
      • Check Coupon Validity
      • Apply Loyalty Points
      • Remove Loyalty Points
      • Preview Rewarded Points
      • List Redemption Methods for Cart
      • Customer’s Loyalty Wallet
      • Customer’s Current Points Balance
    • Account
      • Get Profile
      • Delete Account
      • Update Customer Profile
      • Get Addresses
      • Create an Address
      • Get an Address by ID
      • Update an Existing Address
      • Delete Address
      • Get Orders
      • Get Shareable Wishlist Link
      • Get Wishlist
      • Add Products to Wishlist
      • Remove Product from Wishlist
      • Get Address Form Schema
      • Check Product Purchase Status
    • Storefront
      • Store Scripts
      • Pages
      • Blogs
    • Countries
      • Get Countries
      • Get Cities By Country
  1. Legacy Theme Migration

Twig to Jinja

Twig ➜ Jinja Migration Guide#

1) Overview#

A practical cheat sheet to move templates from Twig to Jinja, with filename rules, filter/tag equivalents, globals, syntax tips, translation changes, and common include mappings.

2) File and variable names#

Rename all .twig and .html files to .jinja.
following the standard python snake_case naming for variables, files, marcors, etc.. is preffered for better code readablity. However this isn't a strict rule.

3) Filters — Direct Equivalents#

TwigJinjaNotes
rawsafeMarks string as safe HTML so jinja doesn't double-escape.
json_encode()tojsonUseful for embedding data in <script>.
filterselect / rejectJinja uses tests/expressions: items | selectattr('x', 'equalto', 1) etc.
url_encodeurlencodePercent-encodes strings.

4) Helper Functions → Jinja Filters#

In many cases, function-style helpers in Twig become filters in Jinja.
Twig (function/helper)Jinja (filter)Notes
asset_urlasset_urlSame name (environment-provided).
assetUrlasset_urlSwitch to snake_case.
strReplace(haystack, search, replace)haystack | replace(search, replace)Jinja replace takes (old, new).
imageUrl(store_partner.image,{ w: 250, q: 100, f:'auto' })image_url(store_partner.image, w=250, q=100, f='auto')Jinja references the params directly and switch to snake_case.
Prebound context helpers
NameWhat it doesTypical usage
today()Current date in the request’s timezone, preformatted{{ today() }}
now()Current time in the request’s timezone{{ now() }}
format_datetimeDateTime formatter with tz/locale already set{{ format_datetime(order.created_at) }}
format_timeTime formatter with tz/locale already set{{ format_time(order.created_at) }}
url_forApplication route builder{{ url_for('list_products') }}

Usage examples#

{# Current date (uses prebound fmt) #}
{{ today() }}                     {# e.g., 2025/08/20 #}

{# Current time #}
{{ now() }}                       {# e.g., 17:05 #}

{# Format a specific datetime with defaults (tz/locale are prebound) #}
{{ format_datetime(order.created_at) }}

{# Choose a predefined width: "full" | "long" | "medium" | "short" #}
{{ format_datetime(order.created_at, fmt="long") }}

{# Override the format string if needed #}
{{ format_datetime(order.created_at, fmt="%Y-%m-%d %H:%M") }}

{# Format only the time portion #}
{{ format_time(order.created_at) }}

Notes#

If datetime_/time_ is omitted (None), the helpers use “now” in the request’s timezone.
fmt accepts "full" | "long" | "medium" | "short" or a custom format string.
tz and locale are already supplied via partial application; override only when you truly need to.

5) Globals & URL Utilities#

Twig Global / HelperJinja / App EquivalentExample
requestAdd(query: dict)session.url.include_query_params(**params)session.url.include_query_params(page=2)
requestGetsession.query_params.get()session.query_params.get('q')
requestUrisession.url.path/products/123
requestInputssession.url.paramsDict-like of current query.
urlWithQuerysession.url.include_query_params(**params)—
rangeNPython range(n)range(5) → 0..4
rangeNWithStepPython range(start, stop, step)range(0, 10, 2)
tDate— (no direct equivalent)Use Babel date/time formatters.
—url_for(name, *, localize=True, query_params=None, **path_params)Returns app route: url_for('list_products')
url_for is provided by your app. Use query_params to append query strings.

6) Data: Pagination Key Mapping#

ConceptTwig (old)Jinja (new)
Itemsresponse.dataresponse.results
Total countresponse.totalresponse.count
Current pageresponse.current_pageresponse.page
Page size / per pageresponse.per_pageresponse.page_size
Total pagesresponse.last_pageresponse.pages_count

7) Syntax Differences & Tips#

7.1 Null safety / defaults#

TaskTwigJinjaNotes
Default valuevar ?? 'x'var or 'x' or var | default('x')Short alias: | d('x')
Force default on falsy—default('x', true)Treats None,'' ,0,[],False as “missing”.
Null literalnullNone—
Examples
{{ title or 'Untitled' }}
{{ title | default('Untitled') }}
{{ count | default(0, true) }}

7.2 Include with a small local context#

TwigJinja
{% include 'x' with {a: 1} %}{% with a=1 %}{% include 'x' %}{% endwith %}

7.3 Translations#

Old: locals.hello_world
New: _("Hello world")
Variable placeholders (gettext style):
{{ _("Hello %(name)s", name=user.name) }}
{{ ngettext("%(n)s item", "%(n)s items", n, n=n) }}

8) Settings Schema Extraction#

Move the JSON inside {% schema %}...{% endschema %} to a separate {}.schema.json file with the same base filename.
Remove the schema block from the .jinja template.

9) Translations Migration (JSON ➜ PO)#

1.
Replace all locals.* usages with _('English text') using strings from en.json.
2.
Generate a .po file and migrate Arabic strings from ar.json into that PO.
3.
Replace runtime formatting args with gettext placeholders:
Before: _("Welcome, {}").format(name)
After: _("Welcome, %(name)s", name=name)

10) Template Conversion (Vitrin-managed Includes)#

When including or extending vitrin-managed templates, prefix with vitrin:{template}.
Old (Twig-like)New (Jinja)
{{ template_for_product_badge }}{% include 'vitrin:products/badge.jinja' %}
{{ template_for_product_variants_list }}{% include 'vitrin:products/variants_list.jinja' %}
{{ template_for_product_variants_dropdown }}{% include 'vitrin:products/variants_dropdown.jinja' %}
{{ template_for_product_payments_widget }}{% include 'vitrin:products/payment_widgets.jinja' %}
{{ template_for_product_custom_input_fields }}{% include 'vitrin:products/custom_input_fields' %}
{{ template_for_product_loyalty_points_widget }}{% include 'vitrin:products/loyalty_points_widget.jinja' %}
{{ template_for_product_apple_pay_button }}{% include 'vitrin:checkout/apple-pay-quick-checkout.jinja' %}
{{ template_for_product_metafields }}{% include 'vitrin:shared/metafields.jinja' %}
{{ template_for_product_grouped }}{% include 'vitrin:products/bundle-products.jinja' %}
{{ template_for_products_badge }}{% include 'vitrin:products/badge.jinja' %}
{{ template_for_products_attributes }}{% include 'vitrin:products/filters.jinja' %}
{{ account_profile_template_content|safe }}{% include 'vitrin:account/profile.jinja' %}
{{ template_for_mazeed_badge }}{% include 'vitrin:shared/mazeed_badge.jinja' %}
{{ template_for_cart_products_list }}{% include 'vitrin:cart/products_list.jinja' %}
{{ template_for_cart_payments_widget }}{% include 'vitrin:cart/payment_widgets.jinja' %}
{{ template_for_cart_gifts_widget }}{% include 'vitrin:cart/gift-card.jinja' %}
{{ template_for_category_metafields }}{% include 'vitrin:shared/metafields.jinja' %}
{{ template_for_blogs_metafields }}{% include 'vitrin:shared/metafields.jinja' %}
{{ template_for_shipping_destination_currency_lang }}{% include 'vitrin:shared/locale_modal.jinja' %}

11) Quick Examples#

Replace strReplace:#

{# Twig-ish #}
{{ strReplace(title, 'old', 'new') }}

{# Jinja #}
{{ title | replace('old', 'new') }}

Build a URL with extra query params:#

{{ session.url.include_query_params(page=page+1) }}

Route generation:#

{{ url_for('list_products') }}
{{ url_for('product_details', path_params={'id': product.id}) }}

Date/time formatting (Babel):#

{{ format_datetime(order.created_at, format='medium', locale=session.lang) }}
{{ format_time(order.created_at, format='short') }}

12) Gotchas#

select/reject rely on tests/expressions (e.g., selectattr, rejectattr, equalto).
Prefer gettext placeholders (%(name)s) over string concatenation for translatable messages.

Modified at 2026-02-17 07:02:10
Previous
Moving to Vitrin Using LLMs
Next
Breaking Changes
Built with