@revkeen/react
v0.1.0-beta.0
Published
React cart hooks and headless display components (ProductGrid, ProductCard, PriceDisplay, BuyButton, CartButton) for selling RevKeen products from Framer, Lovable, v0, Bolt or any React app. Publishable-key browser SDK only.
Readme
@revkeen/react
Not yet published to npm.
npm install @revkeen/reactdoes not work today (REV-7769). Inside this monorepo, depend on it withworkspace:*. For Framer, use the paste-in code components inframer/— they need no npm install.
Framework-neutral React cart hooks, UI primitives and headless display
components for selling RevKeen products from any React app — including apps
generated by Lovable, v0 or Bolt. Works in any React app (no Next.js required, no CMS
assumptions); all API calls go through the generated @revkeen/sdk client
with a publishable key — rk_pk_* is enforced and secret keys are
rejected, so no secret-key operation is importable from this package.
Setup
import { CartProvider } from "@revkeen/react";
<CartProvider publishableKey={import.meta.env.VITE_REVKEEN_PUBLISHABLE_KEY}>
<App />
</CartProvider>Options: baseUrl (default https://api.revkeen.com/v2), checkoutBaseUrl
(default https://pay.revkeen.com), currency, storageKey, storage,
fetch.
Hooks
const { cart, count, status, disabled, error,
addItem, updateQuantity, removeLine, applyDiscount, checkout } = useCart();
const badge = useCartCount();
const { products, loading } = useProducts({ limit: 50 });
const { product } = useProduct("beginner-wheel-course");status:idle | loading | ready | error | disabled.disabledis the dedicatedCART_DISABLEDstate (merchant hasn't enabled Cart) — render a clear disabled UI rather than a generic error.checkout()converts the cart server-side (prices/availability are revalidated by RevKeen) and returns the hosted checkout URL; navigation is yours:const url = await checkout(); if (url) location.href = url;- Products always carry
availability; missing data normalizes tostatus: "unknown"(render nothing rather than guessing stock).
Components
<AddToCartButton productId={p.id} product={p} className="btn">
Add to cart
</AddToCartButton>Minimal and composable — no styling beyond your props. Disables itself with a reason (title + label) when the product is sold out or Cart is disabled.
Display components
Headless-first: every component accepts className, and most accept a render
function as children. Prices always come from the RevKeen API in minor units
and are only formatted (exponent-aware, no float maths) — no component
computes an amount, tax or currency, and a price that cannot be formatted is
not shown rather than guessed.
import { ProductGrid, ProductCard, PriceDisplay, BuyButton, CartButton } from "@revkeen/react";
import "@revkeen/react/styles.css"; // optional structural skin
<ProductGrid productIds={["beginner-wheel-course", "glazing"]} mode="cart" />
<ProductCard product={p} mode="checkout" /> // Buy now → hosted checkout
<PriceDisplay product={p} fallback="Ask for a price" />
<BuyButton productId={p.id} product={p}>Buy now</BuyButton>
<CartButton onOpen={() => setCartOpen(true)} /> // your cart UI via useCart()| Component | Does | Notes |
|---|---|---|
| ProductGrid | Lists active, priced products | productIds (UUID, reference or slug) filters and orders; aria-busy while loading; role="alert" on failure |
| ProductCard | Image, name, price, availability, action | mode: cart (default), checkout, none; named by its heading |
| PriceDisplay | Formats the selected price | priceId, locale, fallback; render prop receives { kind, text, amount } |
| BuyButton | Straight to hosted checkout | Creates a one-product cart and converts it; the shopper's basket is not touched. onCheckout(url) returning false suppresses navigation |
| CartButton | Cart trigger with live count | onOpen opens your cart UI; without it the button converts the cart. The count is announced through a polite live region |
styles.css is structural only (grid, spacing, focus ring). Colours, fonts
and sizes inherit from your page; it introduces no palette. Motion is
disabled under prefers-reduced-motion.
Architecture
All behavior lives in the framework-free CartStore (exported for advanced
use and fully unit-tested in node); the hooks are a thin
useSyncExternalStore wrapper. Cart identity persists in localStorage
(configurable) and survives reloads; expired carts reset silently.
