# Storefront and admin extensions

STOREFRONT AND ADMIN EXTENSIONS

An app on LaunchMyStore isn't just a remote service — it can ship 11 different
extension types that install onto a merchant's store. Storefront blocks render
inside the theme. Admin blocks appear inside the merchant admin. Six declarative
functions run server-side during checkout. This guide walks through every
extension type and where each one shows up.


THE 11 EXTENSION TYPES

 1.  Storefront block — a Liquid block (.aqua) merchants drop into a theme
     section.
 2.  Storefront snippet — a reusable Liquid include other Liquid files
     reference.
 3.  Checkout extension — UI injected at one of checkout's extension points.
 4.  Post-purchase extension — a page rendered immediately after order
     placement.
 5.  Admin block — iframe injected at an admin extension target.
 6.  Cart transform function — rewrites cart line items during cart validation.
 7.  Shipping rate function — emits custom shipping rates.
 8.  Payment customization function — hides/renames/reorders payment methods.
 9.  Delivery customization function — hides/renames/reorders shipping options.
 10. Order validation function — blocks order placement on rule violation.
 11. Discount function — emits declarative discount entries.

Functions are covered in depth in our app functions guide
[https://docs.launchmystore.io]; this article focuses on the first five surfaces
and on the install/uninstall mechanics.


STOREFRONT BLOCK (.AQUA)

A storefront block is a small Liquid file plus a JSON schema. Merchants add it
to a section in the theme editor; the block renders inline with the theme.

{% comment %} blocks/reviews-widget.aqua {% endcomment %}
<div class="reviews-widget" data-product="{{ product.id }}">
  <h3>{{ block.settings.heading }}</h3>
  <!-- review content -->
</div>

// blocks/reviews-widget.schema.json
{
  "name": "Reviews widget",
  "target": "product",
  "settings": [
    { "id": "heading", "type": "text", "default": "Customer reviews" }
  ]
}

The target declares which template the block is eligible for (product,
collection, index, etc.). Settings appear in the theme editor sidebar.


STOREFRONT SNIPPET

Snippets are reusable fragments other Liquid files can {% render %}. Useful for
shared components like a star-rating row. They install into the store’s snippet
directory and themes pull them in with the standard {% render %} rules.


CHECKOUT EXTENSION

Checkout extensions inject UI at named extension points during the checkout flow
(after shipping address, before payment, etc.). They consume cart and customer
state and can present custom fields, banners, or upsells. The checkout host
loads them via CheckoutExtensionSlot and supports App Bridge for host-side
actions.


POST-PURCHASE EXTENSION

Post-purchase extensions render after order placement but before the order
confirmation page. The classic use case is a single-click upsell: "Add this
complementary item for 20% off?" The extension has access to the just-placed
order and can append additional items to it server-side. Loaded via
PostPurchaseExtensions.


ADMIN BLOCK

Admin blocks are iframes embedded in the merchant admin at one of 26 verified
injection points:

 * product.details.block, order.details.block, customer.details.block,
   collection.details.block
 * discount.details.block, gift-card-list, blog-list, contact-list
 * analytics, sales-analytics, inventory
 * shipping-settings, payment-settings, pos-settings, account-settings
 * collection-list, order-list, abandoned-order-list, order-create
 * ... and more (the full list is in the extensions reference
   [https://docs.launchmystore.io])

Your extension manifest declares its target:

{
  "type": "admin_block",
  "target": "product.details.block",
  "url": "/admin/product-analytics.html",
  "settings": []
}

The host renders an iframe pointing at your url, passes apiKey and host query
parameters, and you bootstrap with App Bridge. The iframe self-resizes by
posting APP_BRIDGE_RESIZE with its matching extensionId.


HOW EXTENSIONS INSTALL

App install is a two-step fan-out:

 1. Install record — LaunchMyStore writes the install with the granted scopes
    and stores the extension manifest against the app.
 2. Storefront deploy — the install triggers POST /api/apps/install-extensions,
    which copies every block, snippet, and asset into the store so the theme
    renderer can include them.

Both steps must succeed. The deploy step means storefront blocks are available
in the theme editor immediately and at render time with the same caching as
theme files.


HOW UNINSTALL WORKS

Uninstall reverses the same fan-out: the extension files are removed from the
store, the OAuth access token is revoked, the install row is marked uninstalled,
and the app receives an app/uninstalled webhook identifying the merchant.


CONFIGURING EXTENSION SETTINGS

Each extension declares its merchant-configurable settings in its schema
(storefront blocks → theme editor sidebar; admin blocks → in-iframe forms;
functions → the app's settings UI). Merchant settings are stored against the
install row and passed to the extension at render or run time.


FAQ


WHERE DO ADMIN EXTENSIONS SHOW UP IN THE MERCHANT ADMIN?

One of 26 injection points across the admin: every detail page (product, order,
customer, collection, discount, blog), every list page (orders, abandoned
orders, contacts, gift cards), and settings pages (shipping, payment, POS,
account, analytics). Each extension's target is declared in its manifest.


CAN A SINGLE APP SHIP MORE THAN ONE EXTENSION?

Yes — that's the normal case. A reviews app might ship a storefront block (the
widget), a snippet (the star icon), an admin block (the moderation queue), and a
discount function (loyalty discounts). They all install together when the
merchant approves.


DO STOREFRONT BLOCKS HAVE ACCESS TO PRODUCT DATA?

Yes — they're Liquid, so they can read every global object the theme can:
product, collection, cart, customer, shop, plus all metafields via the standard
{owner}.metafields.{namespace}.{key} pattern.


HOW DO I UPDATE AN EXTENSION AFTER INSTALL?

Push a new app version from the developer portal. Merchants on auto-update
receive the new manifest immediately; those who paused auto-update see an
"Update available" prompt. The install fan-out runs again to refresh the on-disk
files.


WHY IS MY ADMIN BLOCK IFRAME STUCK AT 200PX?

Almost always an extensionId mismatch on the resize message. Your iframe URL has
an ?extensionId= query parameter; you must echo that exact value back in the
APP_BRIDGE_RESIZE payload, otherwise the host ignores the resize.


CAN EXTENSIONS SHARE STATE ACROSS THE ADMIN AND STOREFRONT?

Yes, via metafields. Your admin extension writes a metafield (e.g.
my_app.product_badge) using your write_metafields scope; your storefront block
reads it via product.metafields.my_app.product_badge. Metafields are the
canonical bridge between the two surfaces.