The @kelviq/react-promotions-ui SDK provides React components for displaying location-based promotional banners and dynamically discounted prices. It handles API communication, discount calculation, and currency formatting out of the box.
- A React Context Provider (
KQProvider) to fetch and share promotion data
- A banner component (
KQBanner) with template variables and full styling control
- Price components for formatted display of discounted prices
- Granular price components for building fully custom price layouts
This SDK is separate from the entitlements React SDK and the React UI SDK. Use this SDK when you need PPP / country-based discount banners and price display in a React app.
Installation
npm install @kelviq/react-promotions-ui
# or
yarn add @kelviq/react-promotions-ui
Requires react and react-dom as peer dependencies (v17, v18, or v19).
Quick start
Wrap your app with KQProvider and place KQBanner where you want the banner to appear.
import { KQProvider, KQBanner } from '@kelviq/react-promotions-ui';
function App() {
return (
<KQProvider
promotionId="YOUR_PROMOTION_ID"
accessToken="YOUR_ACCESS_TOKEN"
>
<KQBanner />
{/* Your app components */}
</KQProvider>
);
}
Never expose your Server API Key in the browser. Use the Client API Key, which is safe for client-side code. You can find both keys under Settings → API keys in the Kelviq dashboard.
KQProvider
The provider initializes the SDK, fetches promotion data from the Kelviq API, and makes it available to all child components via React Context.
Props
| Prop | Type | Default | Description |
|---|
promotionId | string | '' | The promotion ID from the Kelviq dashboard |
accessToken | string | '' | Client API Key (Settings → API keys) |
kqIdentifier | string | '' | Optional Kelviq identifier for the request |
environment | 'production' | 'sandbox' | 'production' | Set to 'sandbox' to use the sandbox API |
baseCurrencyCode | string | 'USD' | ISO 4217 currency code used for formatting |
baseCurrencySymbol | string | '$' | Currency symbol for display |
config | object | {} | Advanced configuration (see below) |
Advanced config
The optional config prop accepts:
| Field | Type | Default | Description |
|---|
apiUrl | string | — | Override the default API endpoint |
onError | (error: Error) => void | — | Error callback for API failures |
maxRetries | number | 3 | Maximum retry attempts for failed requests |
timeout | number | 5000 | Request timeout in milliseconds |
Sandbox
Set environment to 'sandbox' to test against the Kelviq sandbox environment:
<KQProvider
promotionId="YOUR_PROMOTION_ID"
accessToken="YOUR_SANDBOX_ACCESS_TOKEN"
environment="sandbox"
>
{/* ... */}
</KQProvider>
KQBanner
Displays a location-aware promotional banner. The banner message is configured in the Kelviq dashboard and supports template variables that are automatically replaced with real data.
import { KQBanner } from '@kelviq/react-promotions-ui';
// Basic — uses styles from the dashboard
<KQBanner />
// With local style overrides
<KQBanner
backgroundColor="#1a1a2e"
fontColor="#eaeaea"
borderRadius="8px"
fontSize="14px"
addCloseIcon={true}
className="my-banner"
onClose={() => console.log('Banner closed')}
/>
Props
| Prop | Type | Default | Description |
|---|
backgroundColor | string | (from API) | Banner background color |
fontColor | string | (from API) | Banner text color |
borderRadius | string | (from API) | Banner border radius |
fontSize | string | (from API) | Banner font size |
unStyled | boolean | (from API) | If true, no default styles are injected |
addCloseIcon | boolean | (from API) | Show a close button on the banner |
className | string | '' | Custom CSS class name |
onClose | () => void | — | Callback fired when the banner is closed |
Props override the corresponding values returned by the API. If you don’t pass a prop, the dashboard-configured value is used.
Template variables
The banner message (configured in the dashboard) supports these placeholders:
| Variable | Replaced with | Example |
|---|
{country} | Visitor’s country name | United States |
{country_flag} | Country flag image (CDN-hosted SVG) | 🇺🇸 (rendered as <img>) |
{coupon_code} | The discount coupon code | SAVE20 |
{discount_percentage} | The discount percentage | 20 |
Example message:
Hello {country_flag} You're visiting from {country}! Use code "{coupon_code}" for {discount_percentage}% off.
CSS hooks
The banner adds classes to document.body that you can target in your stylesheets:
| Class | Added when |
|---|
kq-has-banner | The banner is visible on the page |
/* Add top padding so content isn't hidden behind the banner */
body.kq-has-banner main {
padding-top: 60px;
}
Unstyled mode
Set unStyled={true} to remove all default styles and apply your own CSS from scratch. Target the banner using the kq-banner CSS class or the data-testid="kq-banner" attribute.
<KQBanner unStyled={true} className="my-custom-banner" />
Displays a fully formatted discounted price based on the visitor’s location.
import { KQPriceFormatted } from '@kelviq/react-promotions-ui';
// Discounted price
<KQPriceFormatted price={99.99} />
// With decimals
<KQPriceFormatted price={99.99} showDecimal={true} />
// Currency code instead of symbol
<KQPriceFormatted price={99.99} currencyDisplay="code" />
Props
| Prop | Type | Default | Description |
|---|
price | number | — | Required. The product price |
displayPrice | number | — | The price shown for original display (used with isOriginalDisplay) |
isOriginalDisplay | boolean | false | Renders as strikethrough original price for comparison |
className | string | '' | Custom CSS class name |
currencyDisplay | 'symbol' | 'code' | 'name' | 'symbol' | How the currency is displayed |
showDecimal | boolean | false | Whether to show decimal digits |
minimumDecimalDigits | number | 2 | Minimum decimal digits when showDecimal is true |
maximumDecimalDigits | number | 2 | Maximum decimal digits (max 2) |
Original + discounted price
Show a strikethrough original price next to the discounted price:
{/* Original price (strikethrough) */}
<KQPriceFormatted
price={99.99}
displayPrice={99.99}
isOriginalDisplay={true}
showDecimal={true}
/>
{/* Discounted price */}
<KQPriceFormatted price={99.99} showDecimal={true} />
Granular price components
Use these components individually to build custom price layouts with full styling control. All components must be rendered inside a KQProvider.
KQPriceInteger
Displays just the integer part of the discounted price.
import { KQPriceInteger } from '@kelviq/react-promotions-ui';
<KQPriceInteger price={99.99} />
| Prop | Type | Default | Description |
|---|
price | number | — | Required. The product price |
className | string | '' | Custom CSS class name |
KQPriceDecimal
Displays just the decimal portion of the discounted price.
import { KQPriceDecimal } from '@kelviq/react-promotions-ui';
<KQPriceDecimal price={99.99} />
| Prop | Type | Default | Description |
|---|
price | number | — | Required. The product price |
className | string | '' | Custom CSS class name |
minimumDecimalDigits | number | 2 | Minimum decimal digits |
maximumDecimalDigits | number | 2 | Maximum decimal digits (max 2) |
KQPriceDecimalSeparator
Renders the locale-specific decimal separator (e.g. . or ,).
import { KQPriceDecimalSeparator } from '@kelviq/react-promotions-ui';
<KQPriceDecimalSeparator price={99.99} />
| Prop | Type | Default | Description |
|---|
price | number | — | Required. The product price |
className | string | '' | Custom CSS class name |
KQPriceCurrencySymbol
Renders the currency symbol (e.g. $, €, ₹).
import { KQPriceCurrencySymbol } from '@kelviq/react-promotions-ui';
<KQPriceCurrencySymbol />
| Prop | Type | Default | Description |
|---|
className | string | '' | Custom CSS class name |
KQPriceCurrencyCode
Renders the currency code (e.g. USD, EUR, INR).
import { KQPriceCurrencyCode } from '@kelviq/react-promotions-ui';
<KQPriceCurrencyCode />
| Prop | Type | Default | Description |
|---|
className | string | '' | Custom CSS class name |
Custom price layout example
Combine granular components to build a fully custom price display:
import {
KQPriceCurrencySymbol,
KQPriceInteger,
KQPriceDecimalSeparator,
KQPriceDecimal,
KQPriceCurrencyCode,
} from '@kelviq/react-promotions-ui';
<div className="custom-price">
<KQPriceCurrencySymbol />
<KQPriceInteger price={99.99} />
<KQPriceDecimalSeparator price={99.99} />
<KQPriceDecimal price={99.99} />
<span className="currency-code">
<KQPriceCurrencyCode />
</span>
</div>
Complete example
import {
KQProvider,
KQBanner,
KQPriceFormatted,
KQPriceCurrencySymbol,
KQPriceInteger,
KQPriceDecimalSeparator,
KQPriceDecimal,
} from '@kelviq/react-promotions-ui';
function PricingPage() {
return (
<KQProvider
promotionId="YOUR_PROMOTION_ID"
accessToken="YOUR_ACCESS_TOKEN"
>
{/* Promotional banner */}
<KQBanner />
{/* Formatted price with original + discounted */}
<div className="pricing">
<span className="original">
<KQPriceFormatted
price={99.99}
displayPrice={99.99}
isOriginalDisplay={true}
showDecimal={true}
/>
</span>
<span className="discounted">
<KQPriceFormatted price={99.99} showDecimal={true} />
</span>
</div>
{/* Custom price layout with granular components */}
<div className="custom-price">
<KQPriceCurrencySymbol />
<KQPriceInteger price={49.99} />
<KQPriceDecimalSeparator price={49.99} />
<KQPriceDecimal price={49.99} />
</div>
</KQProvider>
);
}
Support
For technical support and questions, reach out at hi@kelviq.com.