---
title: Store storefront settings where only your app can write them
description: >-
  Store every value that your theme app extension renders in an app-owned
  (`$app`) or app-data metafield, so that only your app can change it.
source_url:
  html: >-
    https://shopify.dev/docs/apps/build/security/storefront/store-settings-in-app-owned-metafields
  md: >-
    https://shopify.dev/docs/apps/build/security/storefront/store-settings-in-app-owned-metafields.md
---

# 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.

***

## Why

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.

***

## How

### Store the values in an app-owned or app-data metafield

* [App-owned metafields](https://shopify.dev/docs/apps/build/metafields#app-owned-metafields) use the reserved `$app` namespace (`app` in `shopify.app.toml`). Their admin access defaults to `MERCHANT_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](https://shopify.dev/docs/apps/build/metafields#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`](https://shopify.dev/docs/api/liquid/objects/app).

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.

### Read 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](https://shopify.dev/docs/apps/build/online-store/theme-app-extensions/configuration#reserved-prefixes-for-metafields-and-metaobjects) documents:

```liquid
{{ product.metafields["$app:settings"].badge_text.value | escape }}
```

The prefix resolves only inside your extension. A merchant's own theme code uses the full namespace, `app--{your-app-id}--settings`.

### Migrate from a public namespace

If your extension reads from a public namespace today:

1. Declare app-owned definitions for the values in your `shopify.app.toml` file, and deploy them.

2. Copy each store's existing values into the `$app` namespace with the [`metafieldsSet`](https://shopify.dev/docs/api/admin-graphql/latest/mutations/metafieldsSet) mutation.

3. Update your extension to read from `$app`, with a temporary fallback for stores you haven't copied yet:

   ```liquid
   {%- liquid
     assign badge = product.metafields["$app:settings"].badge_text.value
     if badge == blank
       assign badge = product.metafields.my_app.badge_text.value
     endif
   -%}
   <span class="badge">{{ badge | escape }}</span>
   ```

4. 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 `$app` value is blank.

***

## Verify

No app security check covers this practice. Search your extension's Liquid for metafield reads from namespaces other than `$app` and `app.metafields`.

***
