The Cart
👍 Heads up
If you are using Starter Kit, there's no need need to manually create the Sections and Components below.
All the code examples below are just an implementation suggestions. For brevity, the styles are omitted.
For this guide, we will be creating Sections and Components locally. Make sure you have your Local Development Environment ready.
👉 Remember to commit and push newly created Sections and Components to your remote repository to sync the changes with Shogun Frontend.
It’s common to have a cart open as a drawer, sliding from right to left to indicate a product was added to the cart. This allows the customer to edit the quantity without navigating away from their current page.
This section will cover the cart drawer and normal cart page. The cart page isn't a necessity, but it can serve as a landing page for the customer if they navigate back from the Shopify checkout page.
For both scenarios, we need specific data points:
-a list of products in the cart
-the total price of the products
-a link to redirect us to checkout itself.
Luckily, the frontend-checkout
package provides all of this information via the useCartState
hook.
This guide will cover creating:
-CartItem
: responsible for rendering a single row of a product in the cart.
-CartItems
: shows a list of CartItem
s.
-CartDrawer
: a section that will act as a wrapper and will handle the drawer itself.
Creating CartItem
The CartItem
component accepts a product as a prop coming from frontend-checkout
's useCartState
hook, which we will cover in the next step.
components/CartItem/index.js
// CartItem component
import * as React from 'react'
import { useCartActions } from 'frontend-checkout'
import { IconButton } from '@chakra-ui/react
import Link from 'frontend-link'
import NumberInput from 'Components/NumberInput'
const CartItem = ({ product }) => {
const { id, quantity, title: name, variant } = product
const { image, price, product: variantProduct, title: variantTitle } = variant
const { src } = image
const { handle: slug } = variantProduct
const { removeItems, updateItems } = useCartActions()
const onChangeItemQuantity = (_, quantityAsNumber) => {
updateItems({ id, quantity: quantityAsNumber })
}
return (
<div>
<div>
<Link to={`/products/${slug}`}>
<div>
<img alt="" src={src} height="120px" width="180px" sizes="120px" />
</div>
<div>
<h3>
{name}
</h3>
{variantTitle && (
<p>
{variantTitle}
</p>
)}
</div>
</Link>
</div>
<p>
${price}
</p>
<div>
<NumberInput
defaultValue={quantity}
onChange={onChangeItemQuantity}
inputProps={{
'aria-label': 'Product quantity',
size: 'xs',
}}
buttonProps={{
size: 'xs',
}}
/>
</div>
<p>
${Number(price) * quantity}
</p>
<div>
<IconButton
icon={<Icon icon="CloseIcon" />}
aria-label={`Remove ${name} from cart`}
border="0"
borderRadius="0"
size="xs"
variant="outline"
_hover={{ color: 'gray.700' }}
onClick={() => removeItems(id)}
/>
</div>
</div>
)
}
export default CartItem
Creating CartItems
The useCartState
hook will give us access to all products a customer may have in their cart, the subtotalprice
, and the checkoutUrl
.
components/CartItems/index.js
// CartItems component
import * as React from 'react'
import { useCartState } from 'frontend-checkout'
import Link from 'frontend-link'
import CartItem from 'Components/CartItem'
const CartItems = () => {
const { checkoutUrl, items, subtotalPrice } = useCartState()
return (
<div>
<p>
Shopping cart ({items.length})
</p>
{items.length ? (
<React.Fragment>
{items.map(product => (
<div key={product.id}>
<CartItem product={product} />
<hr />
</div>
))}
<div>
<div>
<strong>Subtotal:</strong>
<p>${subtotalPrice}</p>
</div>
<div>
<Link to="/">
Continue shopping
</Link>
<Link to={checkoutUrl}target="_self">
Checkout
</Link>
</div>
</div>
</React.Fragment>
) : (
<React.Fragment>
<p>Your cart is currently empty.</p>
<Link to="/">
Start shopping
</Link>
</React.Fragment>
)}
</div>
)
}
export default CartItems
Creating CartDrawer
This step will assemble everything in the CartDrawer
section. We will use hideCart
from useCartActions
and isCartShown
from the useCartState
hook and have the showCart
function to display the CartDrawer
when a product is successfully added to the cart.
sections/CartDrawer/index.js
// CartDrawer section
import * as React from 'react'
import { useCartActions, useCartState } from 'frontend-checkout'
import CartItems from 'Components/CartItems'
import Drawer from 'Components/Drawer'
const CartDrawer = () => {
const { hideCart } = useCartActions()
const { isCartShown } = useCartState()
return (
<Drawer isOpen={isCartShown} onClose={hideCart} size="md">
<CartItems />
</Drawer>
)
}
export default CartDrawer
The CartDrawer
section will live inside the App
component. If you are using the Starter Kit, an App
component already exists and you will just need to import the CartDrawer
into it:
components/App/index.js
import React from 'react'
import { ChakraProvider } from '@chakra-ui/react'
import { theme } from 'Components/Theme'
import CartDrawer from 'Components/CartDrawer'
const App = props => {
const { children } = props
return (
<ChakraProvider theme={theme}>
<CartDrawer />
{children}
</ChakraProvider>
)
}
export default App
You will now be able to see the CartDrawer
if you add a product to the cart.
The Cart Page
Similar to the CartDrawer
, the Cart page will also show a list of the products and will use CartItems
.
As a wrapper to the CartItems
, we will create a section called CartPage
and call the CartItems
:
sections/CartPage/index.js
import * as React from 'react'
import CartItems from 'Components/CartItems'
import Container from 'Components/Container'
const CartPage = () => (
<Container>
<CartItems />
</Container>
)
export default CartPage
Next, create a page called Cart
, making sure to use /cart
for the path, and add the CartPage
section to the Cart page. Reference Adding a section in the Homepage Guide if an example is needed.
The cart page is now complete and your customers can now add products to their cart and checkout!
Multipass Setup (Shopify)
When the checkout is on a subdomain, the headless store and Shopify checkout pages don't share the same authentication sessions. Therefore, login sessions on Frontend won't be reflected on the checkout page. In order to pass on authentication from Frontend to Shopify, we must use multipass. Follow this guide to setup multipass authentication for your store's checkout.
Note that multipass is only necessary when the checkout and Frontend store are on different domains (subdomains)
Prerequisites
Before setting up multipass, the following needs to be setup on your store:
Account Pages, to setup account pages follow these steps
Checkout Sub-domain pointed to Shopify in your DNS provider
Step One: Setup Checkout Subdomain
In your DNS provider, point your subdomain to Shopify by setting up a CNAME record with the value
shops.myshopify.com
In Shopify Admin, go to
Settings > Domains > Connect Existing Domain
Add your checkout subdomain and verify the connection
Set the checkout subdomain as the primary domain
Step Two: Enable Multipass
In Shopify Admin, go to the
Settings > Checkout and Accounts
sectionScroll down to Customer Accounts
Select Enable Multipass. Once enabled, a secret will be shared with you. Make sure you keep your secret private.
Step Three: Send Details to Shogun
Contact Shogun support and create a ticket to enable multipass for your Frontend Store
In the ticket add the following:
The multipass secret, avoid sending the multipass secret in plaintext by using onetimesecret or similar service
Your checkout subdomain
Logout on Checkout Pages
When using multipass, the session created by the multipass login cannot be communicated back to Frontend. Therefore if a user logs out on Shopify, the user will remain logged in on Frontend.
Since the checkout and the headless store are on separate domains (even if subdomains) it’s not possible to retrieve the login state on the subdomain.
There is a workaround for this for Shopify Plus stores by following this process:
In Frontend, create a new page with the path
/account/logout
Create a section called
Logout
Add the following code into the section
import React from 'react'
import { useCustomerState, useCustomerActions } from 'frontend-customer'
const Logout = () => {
const { isLoggedIn } = useCustomerState()
const { logout } = useCustomerActions()
React.useEffect(() => {
logout()
}, [])
React.useEffect(() => {
if (!isLoggedIn) {
window.location.href = 'https://{YOUR CHECKOUT SUBDOMAIN}/account/logout?return_url=/cart'
}
}, isLoggedIn)
return (
<p></p>
)
}
export default Logout
Create a build and publish the store
If you don’t already have a
checkout.liquid
layoutIn Shopify, navigate to
Theme > Edit Code > Add New Layout
Select
checkout
If you already have a
checkout.liquid
layout, add the following snippet above the</body>
tag
<script>
Checkout.$(document).on('page:load', function() {
Checkout.$('a[href*="account/logout"]').attr("href", "https://{YOUR-FE-DOMAIN}/account/logout");
})
</script>
This will log the user out of both Shopify and FE, and will redirect back to the cart.