Access control

Login to see price without editing your theme

Prime Lock Team

11 February 2026 · 6 min read

ACCESS CONTROL
Contents

You sell to trade buyers and to the public from one store. Public visitors should see the product and not the price. Logged-in trade accounts should see both. This is one of the most common requests in Shopify support forums, and there are three answers in circulation. All three are incomplete, in different ways.

Route 1: edit price.liquid

The standard advice. Find snippets/price.liquid, wrap the money output in a customer check, done. It works immediately and it costs nothing.

snippets/price.liquid: the usual edit
{% if customer %}
  <span class="price">{{ product.price | money }}</span>
{% else %}
  <a href="/account/login">Log in to see price</a>
{% endif %}

How it fails

  • Theme updates. The moment you edit a theme file you own the merge. Dawn ships changes to price.liquid regularly; every update becomes a manual diff, and most merchants respond by never updating.
  • Coverage. price.liquid is not the only place a price renders. Cart drawer, search suggestions, quick-add modals, upsell blocks and third-party sections each render their own money value.
  • The JSON endpoint. /products/<handle>.js is served by Shopify, not by your theme. It still returns the price, logged in or not.
  • Uninstalling. Snippets stay behind. Six months later nobody remembers which lines were added.

Route 2: hide it with CSS

The one-line fix that is not a fix
.price, .product__price { display: none; }

This is the fastest option and the worst one. The price is still in the HTML. It is in view-source, it is in the crawl, it is in the JSON-LD, and it is one browser devtools toggle away from being visible. Googlebot renders CSS, so if the price is in the markup and hidden from users, you have built a small, unintentional cloaking case.

Route 3: a Liquid conditional around the buy section

A step up from route one: instead of hiding just the money, wrap the whole buy-buttons block. Logged-out visitors get a login prompt where the add-to-cart button would be. Display coverage is much better, because you are removing the entire purchase affordance rather than one span.

It still has route one's theme-fork problem, and it still has the fundamental issue: removing the button does not remove the endpoint. The form posts to /cart/add. That endpoint is public. Anyone can post a variant ID to it, or skip it entirely with a cart permalink.

What an app embed does instead

App embed blocks are a theme app extension feature. You toggle one switch in Theme Editor → App embeds and the app's script is injected into the theme at runtime. Nothing is written into your theme files, so theme updates are unaffected and uninstalling leaves no residue.

At page load the embed evaluates your rules against the current customer (logged in or not, which tags they carry, whether the resource is locked) and replaces the price and buy section with whatever you configured. Because it runs against every rendered price on the page rather than one snippet, cart drawers and quick-adds are covered too.

Before and after

Before: forked theme, one surface covered
// snippets/price.liquid  (edited by hand, now off-template)
{% if customer %}{{ product.price | money }}{% else %}Log in{% endif %}

// theme update available -> manual merge, every time
// /products/wool-throw.js -> { "price": 4200 }   <- still public
After: app embed on, theme untouched
// theme files: unchanged, updates apply cleanly
// rule: collection "trade" -> visible to customers tagged `wholesale`
// display: price + buy section replaced with "Log in to see price"
// enforcement: checkout validation rejects restricted variants server-side

Why enforcement has to live at checkout

Every approach above, including the app embed's display layer, runs in the browser. Anything that runs in the browser can be skipped by not using the browser the way you expected. The relevant example is the cart permalink:

No product page involved
https://your-store.myshopify.com/cart/44192837465:1

That URL puts the variant in the cart and lands the visitor on the cart page. Your locked product page never rendered, so your gate never ran. Variant IDs are not secret; they are in the product JSON, in the collection JSON, and in the browser history of anyone who was once approved.

Shopify Functions close this. A cart and checkout validation function runs on Shopify's servers on every plan. It inspects the cart contents and the buyer identity, and it can block the checkout with a message you write. There is no client-side workaround because there is no client involved.

So the practical answer to "login to see price without editing my theme" is two layers: an app embed for what visitors see, and a validation function for what they can actually buy. One without the other is either ugly or useless.

The flash of visible price, and how to avoid it

There is one failure mode specific to the app-embed approach that is worth naming, because it is the thing merchants complain about after switching. If the embed has to make a network request to find out which rules apply before it can hide anything, the page renders the real price first, and a moment later the price disappears. On a fast connection it is a flicker. On a phone on mobile data it can be a second of your trade pricing sitting on screen, and it is trivially screenshot-able.

The fix is to have the rules already present when the page renders. Storing lock configuration in shop metafields means the theme has the rules in hand at render time, with no round trip, no waiting and no flash. It is also why this approach does not damage your Largest Contentful Paint: there is no blocking request in the critical path, so the price area resolves in the first paint rather than after it.

When you evaluate any app in this category, this is the question to ask, and it is easy to test. Install it on a development store, throttle the network to Slow 3G in Chrome devtools, and reload a locked product page. If the price appears and then vanishes, the rules are being fetched rather than cached, and every visitor on a poor connection sees the number you are trying to hide.

What to check after you switch

Moving from a theme edit to an app embed is quick, but the verification is the part that matters, and a surprising number of stores skip it. Work through these in order on a real product that is supposed to be locked.

  1. 1View the product page logged out. The price should be replaced by your message, and the add-to-cart button should be gone rather than merely disabled.
  2. 2Open /products/<handle>.js directly in the address bar. This is the endpoint the theme edit could never cover. Confirm the price is not being served to an unauthenticated visitor.
  3. 3Search for the product in the storefront search and open the quick-add modal. Search results and quick-adds render their own price markup and are the most commonly missed surface.
  4. 4Open the cart drawer with a previously added item. Cart line items render prices from a different template again.
  5. 5Build a cart permalink by hand, using /cart/<variant_id>:1, and try to check out. This is the test that separates a display gate from an enforced one. The checkout should be blocked with your message.
  6. 6Log in as a customer carrying the right tag and repeat all five. Everything should now be visible and purchasable.

Then remove the old theme edit. Leaving the Liquid conditional in place alongside an app is the most common cause of a support ticket that begins "the price is hidden for approved customers too", because two systems are making the same decision and only one of them knows about your tags. Find the snippet, restore it from the unmodified theme, and confirm the app is doing the work on its own. If you are not certain which lines were added, duplicate the live theme first, then compare the copy against a fresh download of the same theme version; the diff is usually three or four lines and takes a couple of minutes to read.

Frequently asked

Prime Lock Team

Prime Lock is built by Aspedan, the team behind Discount Prime and Prime MOQ. We write about the parts of Shopify that are harder than they look.

Decide who gets to see your store.

Early access opens with the free plan. Unlimited locks, enforced at checkout.

No newsletter. One email when Prime Lock is live.