Skip to main content

PriceToggle

A lightweight UI toggle that lets users switch between displaying prices including VAT or excluding VAT. The component does not perform price calculations — it broadcasts the user's preference so other components can adjust which price they display.

Usage

Basic toggle in the header

import PriceToggle from '@/components/propeller/PriceToggle';

function Header() {
return (
<PriceToggle
inclExclVatSwitched={(on) => {
localStorage.setItem('price_include_tax', String(on));
}}
/>
);
}

With a custom label and initial state

<PriceToggle
label="Show prices:"
initialState={false}
inclExclVatSwitched={(on) => {
localStorage.setItem('price_include_tax', String(on));
}}
/>

Reading the persisted value on mount

function HeaderToggle() {
const stored = typeof window !== 'undefined'
? localStorage.getItem('price_include_tax') !== 'false'
: true;

return (
<PriceToggle
initialState={stored}
inclExclVatSwitched={(on) => {
localStorage.setItem('price_include_tax', String(on));
}}
/>
);
}

Styled with extra class

<PriceToggle
className="bg-gray-100 rounded px-2 py-1"
inclExclVatSwitched={(on) => {
localStorage.setItem('price_include_tax', String(on));
}}
/>

Configuration

Display

PropTypeRequiredDefaultDescription
labelstringNo'Prices:'Label text shown beside the toggle. Hidden on small screens (hidden sm:inline).
classNamestringNo''Extra CSS class applied to the root element.

State & Callbacks

PropTypeRequiredDefaultDescription
initialStatebooleanNotrueInitial toggle position. true = incl. VAT, false = excl. VAT.
inclExclVatSwitched(on: boolean) => voidYesCallback fired when the toggle is switched. Receives the new state.

Behavior

localStorage persistence

The toggle value is persisted under the localStorage key price_include_tax as a string ('true' or 'false'). The parent component is responsible for writing to localStorage inside the inclExclVatSwitched callback. Listening components read this key on mount to initialize their state.

Custom event dispatch

Every time the toggle is switched, the component dispatches a global custom event:

window.dispatchEvent(new CustomEvent('priceToggleChanged', { detail: newValue }));

detail is a boolean: true for incl. VAT, false for excl. VAT. This event fires automatically — the parent does not need to dispatch it.

Default state

The default state is true (incl. VAT). This matches the initialization value used by all listening components, which prevents hydration mismatches. If you override initialState to false, make sure localStorage already contains 'false' before the page renders, or listening components will briefly show incl. VAT prices until they receive the event.

Rendering

The toggle renders as a <button> with role="switch" and aria-checked for accessibility. It displays either "Incl. VAT" or "Excl. VAT" as text. The label is hidden on screens smaller than the sm breakpoint.

SDK Services

PriceToggle does not call any SDK services directly. It is a pure UI control. However, it dispatches a priceToggleChanged custom event that other components listen for to decide which SDK price field to display.

SDK price mapping

The Propeller SDK uses inverted naming for price fields:

SDK fieldActual meaning
price.netPrice including VAT
price.grossPrice excluding VAT

When the toggle is on (true), components should display price.net (incl. VAT) as the primary price. When the toggle is off (false), components should display price.gross (excl. VAT).

Components that listen for the toggle

ComponentHow it listens
ProductCardInternal priceToggleChanged event listener + _includeTax state
ClusterCardInternal priceToggleChanged event listener + _includeTax state
ProductPriceInternal priceToggleChanged event listener + _includeTax state
ProductBulkPricesInternal priceToggleChanged event listener + _includeTax state
FavoriteListItemInternal priceToggleChanged event listener + _includeTax state
Cluster detail pageInline useState + useEffect listener