A React Hook that handles shopping cart state and logic for Stripe.
npm install --save @stripe/stripe-js use-shopping-cart
# or
yarn add @stripe/stripe-js use-shopping-cart
To run this package for development run:
yarn dev
To run tests:
yarn test
At the root level of your application (or the highest point you'll be using Stripe from), wrap your components in a <CartProvider>
.
<CartProvider>
comes with several props that allow you to interact with the Stripe API and customize the Stripe experience.
When loading up Stripe, don't forget to use your public Stripe API key with it. If you need help setting up your environment variables for this, view a list of environment variable tutorials.
import ReactDOM from 'react-dom';
import { loadStripe } from '@stripe/stripe-js';
import { CartProvider } from 'use-shopping-cart';
import App from './App';
// Remember to add your public Stripe key
const stripePromise = loadStripe(process.env.REACT_APP_STRIPE_API_PUBLIC);
ReactDOM.render(
<CartProvider
stripe={stripePromise}
successUrl="stripe.com"
cancelUrl="twitter.com/dayhaysoos"
currency="USD"
allowedCountries={['US', 'UK', 'CA']}
billingAddressCollection={true}
>
<App />
</CartProvider>,
document.getElementById('root')
);
The hook useShoppingCart()
provides several utilities and pieces of data for you to use in your application. The examples below won't cover every part of the useShoppingCart()
API but you can look at the API below.
import { useShoppingCart } from 'use-shopping-cart';
import { Product } from './Product';
import { CartItems } from './CartItems';
const productData = [
{
name: 'Bananas',
sku: 'sku_GBJ2Ep8246qeeT',
price: 400,
image: 'https://www.fillmurray.com/300/300',
currency: 'USD',
},
{
name: 'Tangerines',
sku: 'sku_GBJ2WWfMaGNC2Z',
price: 100,
image: 'https://www.fillmurray.com/300/300',
currency: 'USD',
},
];
export function App() {
/* Gets the totalPrice and a method for redirecting to stripe */
const { totalPrice, redirectToCheckout, cartCount } = useShoppingCart();
return (
<div>
{/* Renders the products */}
{productData.map((product) => (
<Product product={product} />
))}
{/* This is where we'll render our cart */}
<p>Number of Items: {cartCount}</p>
<p>Total: {totalPrice()}</p>
<CartItems />
{/* Redirects the user to Stripe */}
<button onClick={redirectToCheckout}>Checkout</button>
</div>
);
}
To add a product to the cart, use useShoppingCart()
's addItem(product)
method. It takes in your product object, which must have a sku
and a price
, and adds it to the cart.
import { useShoppingCart, formatCurrencyString } from 'use-shopping-cart';
export function Product({ product }) {
const { addItem } = useShoppingCart();
/* A helper function that turns the price into a readable format */
const price = formatCurrencyString({
price: product.price,
currency: product.currency,
language: navigator.language,
});
return (
<article>
<figure>
<img src={product.image} alt={`Image of ${product.name}`} />
<figcaption>{product.name}</figcaption>
</figure>
<p>{price}</p>
{/* Adds the item to the cart */}
<button
onClick={() => addItem(product)}
aria-label={`Add ${product.name} to your cart`}
>
Add to cart
</button>
</article>
);
}
Once the user has added their items to the cart, you can use the cartDetails
object to display the different data about each product in their cart.
Each product in cartDetails
contains the same data you provided when you called addItem(product)
. In addition, cartDetails
also provides the following properties:
Name | Value |
---|---|
quantity |
Number of that product added to the cart |
value |
The price * quantity |
formattedValue |
A currency formatted version of value |
import { useShoppingCart } from 'use-shopping-cart';
export function CartItems() {
const {
cartDetails,
reduceItemByOne,
addItem,
removeCartItem,
} = useShoppingCart();
const cart = [];
// Note: Object.keys().map() takes 2x as long as a for-in loop
for (const sku in cartDetails) {
const cartEntry = cartDetails[sku];
// all of your basic product data still exists (i.e. name, image, price)
cart.push(
<article>
{/* image here */}
{/* name here */}
<p>Line total: {cartEntry.formattedValue}</p>
{/* What if we want to remove one of the item... or add one */}
<button
onClick={() => reduceItemByOne(cartEntry.sku)}
aria-label={`Remove one ${cartEntry.name} from your cart`}
>
-
</button>
<p>Quantity: {cartEntry.quantity}</p>
<button
onClick={() => addItem(cartEntry)}
aria-label={`Add one ${cartEntry.name} to your cart`}
>
+
</button>
{/* What if we don't want this product at all */}
<button
onClick={() => removeCartItem(cartEntry.sku)}
aria-label={`Remove all ${cartEntry.name} from your cart`}
>
Remove
</button>
</article>
);
}
return cart;
}
Note that in the above code, to reduce the quantity of a product in the user's cart, you must pass an SKU to reduceItemByOne()
like so:
reduceItemByOne(cartEntry.sku);
Just like you can reduce the quantity of a product you can remove the product entirely with removeCartItem()
:
removeCartItem(cartEntry.sku);
However, those two examples differ from the way that you increase the quantity of a product in the user's cart. Currently, to do this, you must pass the entire cartEntry
to addItem()
:
addItem(cartEntry);
You can view the full API on our documentation page.
Props for this component:
Name | Type |
---|---|
stripe |
Stripe | undefined |
successUrl |
string |
cancelUrl |
string |
currency |
string |
language |
string |
billingAddressCollection |
boolean |
allowedCountries |
null | string[] |
Returns an object with all the following properties and methods:
Name | Type/Args | Return Type |
---|---|---|
addItem() |
product: Object | N/A |
reduceItemByOne() |
sku: string | N/A |
removeCartItem() |
sku: string | N/A |
totalPrice() |
N/A | string |
cartCount |
number | N/A |
cartDetails |
Object of cart entries | N/A |
redirectToCheckout() |
sessionId?: string | Error (if one occurrs) |
clearCart() |
N/A | N/A |
This function takes one options argument, these are the options for this function:
Name | Type |
---|---|
value | number |
currency | string |
language | string |
The following tutorials teach how to set up your custom environment variables for your project.
MIT © dayhaysoos
We created this hook with create-react-hook.
Thanks goes to these wonderful people (emoji key):
Thor 雷神 📖 💻 |
|||||
Kevin Cunningham |
Ian Jones |
Christopher Brown 📖 |
Nick DeJesus 💻 |
Shodipo Ayomide 📖 |
Anders Bech Mellson 💻 |
This project follows the all-contributors specification. Contributions of any kind welcome!