Show prices with and without taxes in Shopify
Many business located in some countries (especially in the UE) may need to show prices both including and excluding taxes. This is even more a necessity for those selling in B2B.
This tutorial shows a way to implement this in a dynamic way, based on actual market and taxes configuration. This strategy may be better than hardcoding tax rates in the theme like many community discussions suggest… (👀 https://community.shopify.com/t/how-to-display-price-with-and-without-vat/342718)
View the demo for France (password: brush)
What this feature does
Section titled “What this feature does”It allows customers to see products prices:
- Both including and excluding tax
- In a dynamic way by using actual market and taxes data…
- … rather than hardcoding taxe rates in Liquid files.
Key point: Instead of updating (bloating!) Liquid template files with as may taxe rates avaible, making calculations, breaking the separation of concerns principle, etc… and having to do this for each file for a feature (featured product, product block on list, product info on product detail, search suggestions, …), we do a unique and cached AJAX call to our Brush backend and use elegant Javascript to update all price blocks at once. Whatever the number of blocks displaying prices.
Follow along with the code
Section titled “Follow along with the code”Backend code
Section titled “Backend code”See branch on backend tutorial repo tutorial/taxes-included-excluded and its Excl. tax price calculation commit.
Frontend code
Section titled “Frontend code”See branch on frontend tutorial repo tutorial/taxes-included-excluded and its Excl. tax price calculation commit.
Implementation Overview
Section titled “Implementation Overview”1. Gadget Backend
Section titled “1. Gadget Backend”-
api/routes/brush/tax-rate/GET.ts- Uses the
draftOrderCalculateGraphQL mutation… - … to simulate a 100 order in the current currency.
- Extracts tax-related information.
- Calculates the tax-exclusive amount from the tax-inclusive product price.
- Returns a normalized structure containing the tax rate.
- Uses the
-
Tax Calculation Logic
- Centralized in the backend instead of being duplicated in Liquid templates.
- Uses the actual tax configuration configured in Shopify Markets.
- Ensures consistency with Shopify’s own tax engine.
- Exposes tax information to the storefront through a lightweight endpoint.
2. Shopify Theme Components
Section titled “2. Shopify Theme Components”-
Price Blocks
- Existing price components continue to render normally.
- No need to duplicate tax calculations across:
- Product cards
- Product pages
- Featured products
- Search suggestions
- Collection grids
- Any future component displaying prices
-
Data Attributes
- Price elements expose product and variant pricing information through HTML attributes.
- JavaScript can discover all price blocks automatically.
- Keeps Liquid focused on rendering markup only.
3. Frontend Service
Section titled “3. Frontend Service”-
Taxes Service
- Performs a single AJAX request to the Brush backend.
- Retrieves market tax information once.
- Shares the result across the entire page.
-
DOM Updater
- Scans the page for price blocks.
- Calculates and formats tax-exclusive prices.
- Injects additional UI below existing prices.
- Updates all matching components at once.
-
Cached API Result
- Responses are cached aggressively to LocalStorage thanks to Brush Stores powered by Alpine Store.
- Prevents repeated Shopify Admin API requests from the theme.
4. Presentation Layer
Section titled “4. Presentation Layer”-
Tax-Included Price
- Remains the primary displayed Shopify price.
- Continues to benefit from Shopify formatting and localization.
-
Tax-Excluded Price
- Generated dynamically from backend-provided tax data.
- Displayed alongside the regular price.
- Automatically adapts when the customer changes market.
How It Works (Step-by-Step Recipe)
Section titled “How It Works (Step-by-Step Recipe)”1. Customer Opens a Page
Section titled “1. Customer Opens a Page”- Shopify renders prices normally.
- Product cards and product pages display their standard prices.
- No extra tax calculations are performed in Liquid.
2. Frontend Fetches Market Tax Data
Section titled “2. Frontend Fetches Market Tax Data”-
The storefront performs a single request to the Brush backend.
-
Brush determines:
- The active Shopify market.
- The applicable tax rate.
-
The response is cached for future requests.
3. All Price Blocks Are Updated
Section titled “3. All Price Blocks Are Updated”- The frontend scans the DOM for price components.
- Each price block is enhanced automatically.
- The tax-exclusive amount is displayed below the standard price.
Because the tax information is fetched only once, the cost remains constant whether the page contains:
- 2 products
- 20 products
- 200 products
4. Market Changes Are Handled Automatically
Section titled “4. Market Changes Are Handled Automatically”- If a customer browses from another market:
- Shopify applies the corresponding market configuration.
- Brush retrieves the appropriate tax settings.
- Prices are recalculated using the correct rate.
No Liquid changes are required when:
- Adding a new market
- Changing a VAT rate
- Expanding internationally
5. UI Stays Decoupled
Section titled “5. UI Stays Decoupled”- Shopify remains responsible for product pricing.
- Gadget remains responsible for tax calculations.
- JavaScript remains responsible for presentation.
Each layer focuses on its own concern, making the implementation easier to maintain over time.
Best Practices & Powerful Tips
Section titled “Best Practices & Powerful Tips”-
Avoid hardcoding VAT rates
Tax rates evolve. Markets evolve. Regulations evolve. If you hardcode
20%,21%, or19%directly in Liquid files, you’ll eventually have to hunt them down across your theme. -
Keep tax logic out of Liquid
Liquid is great for rendering HTML. It is much less suited for business logic and calculations spread across multiple components. Centralizing tax calculations keeps templates clean and maintainable.
-
Use Shopify Markets as the source of truth
Shopify already knows which market a customer belongs to. Reuse that information instead of maintaining a separate tax configuration layer.
-
Fetch once, update everywhere
A single cached AJAX request is usually cheaper and easier to maintain than repeating calculations in every template that displays prices.
-
Design for scalability
New product cards, recommendation blocks, quick views, search suggestions, and collection pages automatically benefit from the feature because the frontend enhances all price blocks uniformly.
-
Respect separation of concerns
- Shopify → product and market data
- Gadget → tax calculations
- Theme → rendering
- JavaScript → UI enhancement
This separation makes future maintenance significantly easier.
-
Think beyond VAT
The same pattern can be reused for:
- B2B pricing displays
- Margin indicators
- Wholesale pricing
- Eco-tax breakdowns
- Country-specific pricing information
Once the architecture is in place, additional price-related enrichments become straightforward to implement.
Final Result
Section titled “Final Result”With this approach, customers can instantly see both:
- Tax-inclusive prices
- Tax-exclusive prices
while the store remains fully aligned with Shopify Markets configuration.
Instead of scattering tax formulas throughout the theme, we leverage Shopify as the source of truth, Brush as the calculation layer, and a single frontend enhancement script to update every price component consistently across the storefront.