Store storefront settings where only your app can write them
Store every value that your extension renders in an app-owned or app-data metafield, so that only your app can change it.
A metafield in a public namespace, such as custom or any other namespace that doesn't start with $app, can be changed by other apps installed on the store that have access to the resource, by staff and collaborator accounts that can edit the resource in the Shopify admin, and by translations. For translatable metafield types, a translation registered for a locale through the GraphQL Admin API or a translation app replaces the value that Liquid renders for buyers browsing in that locale. If your extension renders a public metafield as markup, CSS, or JavaScript, then anyone in that list controls what runs on the storefront.
Anchor to Store the values in an app-owned or app-data metafieldStore the values in an app-owned or app-data metafield
- App-owned metafields use the reserved
$appnamespace (appinshopify.app.toml). Their admin access defaults toMERCHANT_READ, so merchants can view the values but not edit them. Translations apply only to publicly accessible metafields, so no other app can translate them. - App-data metafields are stored on your app's installation, hidden from the Shopify admin, and accessible only by your app. Liquid reads them through
app.metafields.
Keep the default MERCHANT_READ access for values that render as markup, CSS, or JavaScript. MERCHANT_READ_WRITE lets staff and collaborator accounts that can edit the resource change the value in the Shopify admin.
App-owned metafields aren't secret. Liquid can read any metafield regardless of its storefront access setting, so don't store sensitive values in them.
Anchor to Read the values in your extensionRead the values in your extension
In your theme app extension, reference an app-owned metafield through the $app reserved prefix and a sub-namespace, which is the form the theme app extension configuration documents:
The prefix resolves only inside your extension. The store's own theme code uses the full namespace, app--{your-app-id}--settings.
Anchor to Migrate from a public namespaceMigrate from a public namespace
If your extension reads from a public namespace today:
-
Declare app-owned definitions for the values in your
shopify.app.tomlfile, and deploy them. -
Copy each store's existing values into the
$appnamespace with themetafieldsSetmutation. -
Update your extension to read from
$app, with a temporary fallback for stores you haven't copied yet:{%- liquidassign badge = product.metafields["$app:settings"].badge_text.valueif badge == blankassign badge = product.metafields.my_app.badge_text.valueendif-%}<span class="badge">{{ badge | escape }}</span> -
After every store's values are copied, remove the fallback and delete the public metafields. Until then, anyone who can write the public metafield controls the value on stores where the
$appvalue is blank.
Anchor to VerifyVerify
The app security checks cover this practice: they report shared-namespace metafield values that reach HTML, JavaScript, or URL output. Run them on every release.