Overview
This guide walks through building a free shipping progress bar component. You’ll learn how to:
Create a component in your project
Access checkout data with the SDK
Run it locally
Register and deploy it to your store
Prerequisites
The Ollie Shop CLI installed (npm install -g @ollie-shop/cli) — see Installation
A scaffolded project (npx create-ollie-shop) — see the Quickstart
Basic React knowledge
Access to a store
A component is a folder under components/ in your project. It is not a
standalone package — ollieshop component create only registers metadata in the
database; it does not scaffold a folder. The actual code lives in your project.
Create the Component
Create the folder
Inside your project, create components/FreeShippingBar/ with an index.tsx
and a meta.json: your-project/
├── components/
│ └── FreeShippingBar/
│ ├── index.tsx # Component code (entry point)
│ ├── styles.module.css # Styles (optional)
│ └── meta.json # Component metadata (id, slot, props)
├── package.json # Project dependencies
└── ...
Add meta.json
meta.json tells the tooling where the component goes. The slot is where it
renders in the checkout (see Slots ). Leave id
out for local development — an unlinked component gets a temporary studio-*
id until you register it.{
"name" : "Free Shipping Bar" ,
"slot" : "your-slot"
}
Build the Component
Create components/FreeShippingBar/index.tsx:
import React from 'react' ;
import { useCheckoutSession } from '@ollie-shop/sdk' ;
import styles from './styles.module.css' ;
export default function FreeShippingBar () {
const { session } = useCheckoutSession ();
const { totals , locale } = session ;
const threshold = 10000 ; // $100 in cents
const subtotal = totals . items ;
const remaining = threshold - subtotal ;
const progress = Math . min (( subtotal / threshold ) * 100 , 100 );
const format = ( cents : number ) =>
new Intl . NumberFormat ( locale . language , {
style: 'currency' ,
currency: locale . currency ,
}). format ( cents / 100 );
if ( remaining <= 0 ) {
return (
< div className = { ` ${ styles . shippingBar } ${ styles . success } ` } role = "status" aria-live = "polite" >
< span > ✓ Free shipping unlocked </ span >
</ div >
);
}
return (
< div className = { styles . shippingBar } role = "status" aria-live = "polite" >
< p > Add { format ( remaining ) } for free shipping </ p >
< div className = { styles . progressTrack } >
< div className = { styles . progressFill } style = { { width: ` ${ progress } %` } } />
</ div >
</ div >
);
}
Add Styles
Create components/FreeShippingBar/styles.module.css:
.shippingBar {
background : #f3f4f6 ;
padding : 12 px ;
text-align : center ;
font-size : 14 px ;
}
.success {
background : #10b981 ;
color : white ;
}
.progressTrack {
background : #e5e7eb ;
height : 6 px ;
border-radius : 3 px ;
margin-top : 8 px ;
overflow : hidden ;
}
.progressFill {
background : #3b82f6 ;
height : 100 % ;
transition : width 0.3 s ease ;
}
Test Locally
Start the local dev server from your project root:
npm start
# or, directly:
ollieshop start
This discovers your components, opens Ollie Studio in the browser, and hot-reloads
as you edit.
Register & Deploy
Register the component
Create the component record in a store version to get its component id . You
can do this in the admin dashboard, or via the CLI: ollieshop component create \
--version-id < version-uui d > \
--name "Free Shipping Bar" \
--slot your-slot
Deploy
Deploy bundles the local components/FreeShippingBar folder and uploads it to
the builder. Pass the component id from the previous step and the folder name: ollieshop deploy \
--component-id < component-uui d > \
--name FreeShippingBar \
--wait
--wait polls until the build finishes.
Verify
ollieshop component list --store-id < store-uui d >
Using Checkout Data
The SDK exposes hooks for reading checkout data. useCheckoutSession returns the
parsed session:
const { session } = useCheckoutSession ();
session . cartItems // Array of cart items
session . totals . items // Subtotal of items, in cents
session . totals . shipping // Shipping cost, in cents (optional)
session . totals . total // Grand total, in cents
session . locale // { currency, language, country }
session . customer // Customer data (optional)
See the hooks reference and the
CheckoutSession type for the full shape.
Adding Interactivity
Cart changes go through useCheckoutAction ,
not useCheckoutSession:
import { useCheckoutAction } from '@ollie-shop/sdk' ;
function RemoveFirstItemButton () {
const { execute , isPending } = useCheckoutAction ( 'REMOVE_ITEMS' , {
onError : ({ serverError }) =>
console . error ( 'Failed to remove item' , serverError ?. message ),
});
return (
< button onClick = { () => execute ([ 0 ]) } disabled = { isPending } >
Remove first item
</ button >
);
}
Best Practices
// Memoize expensive calculations derived from the session
const totalSavings = useMemo (
() =>
session . cartItems . reduce (
( sum , item ) => sum + ( item . originalPrice - item . price ) * item . quantity ,
0 ,
),
[ session . cartItems ],
);
Error Handling
Surface failures to the user via useMessages :
const { addMessage } = useMessages ();
const { execute } = useCheckoutAction ( 'ADD_ITEMS' , {
onError : ({ serverError }) => addMessage ({ type: 'error' , error: serverError }),
});
Accessibility
< div role = "status" aria-live = "polite" >
{ remaining > 0
? ` ${ format ( remaining ) } until free shipping`
: 'Free shipping unlocked' }
</ div >
Next Steps
Custom Component 101 A fully custom CartItem walkthrough
API Reference Complete SDK documentation