<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Charlie Lambert</title>
    <description>The latest articles on DEV Community by Charlie Lambert (@charlielambert).</description>
    <link>https://dev.to/charlielambert</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3881836%2F3c037939-c75c-4bd3-b153-29daed709234.png</url>
      <title>DEV Community: Charlie Lambert</title>
      <link>https://dev.to/charlielambert</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/charlielambert"/>
    <language>en</language>
    <item>
      <title>How to build a checkout page</title>
      <dc:creator>Charlie Lambert</dc:creator>
      <pubDate>Thu, 16 Apr 2026 07:09:14 +0000</pubDate>
      <link>https://dev.to/charlielambert/how-to-build-an-easy-checkout-page-4bi9</link>
      <guid>https://dev.to/charlielambert/how-to-build-an-easy-checkout-page-4bi9</guid>
      <description>&lt;p&gt;The checkout page is the finish line of any e-commerce experience. It requires a delicate balance of handling passed data, calculating totals in real-time, and managing API interactions.&lt;/p&gt;

&lt;p&gt;In this article, we’ll explore how to build a Checkout component that processes a shopping basket and handles order placement with grace.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Accessing the Basket via Router State&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;A common pattern in React is passing data between routes without using a global store like Redux for everything. Here, we use useLocation to grab the basket passed from the shopping cart.&lt;br&gt;
JavaScript&lt;/p&gt;

&lt;p&gt;import { useState } from 'react'&lt;br&gt;
import { useLocation, useNavigate } from 'react-router-dom'&lt;br&gt;
import './Checkout.css'&lt;/p&gt;

&lt;p&gt;function Checkout({ user }) {&lt;br&gt;
  const location = useLocation()&lt;br&gt;
  const navigate = useNavigate()&lt;br&gt;
  const basket   = location.state?.basket || []&lt;br&gt;
  const [success, setSuccess] = useState(false)&lt;br&gt;
  const [error,   setError]   = useState('')&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Calculating the Total&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Before the user clicks "Buy," they need to know the damage. We use the .reduce() method to iterate through the basket and calculate a final total based on price and quantity.&lt;br&gt;
JavaScript&lt;/p&gt;

&lt;p&gt;const total = basket.reduce((sum, item) =&amp;gt; {&lt;br&gt;
    return sum + item.product_price * item.quantity&lt;br&gt;
  }, 0)&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The placeOrder Logic&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The core functionality of this page is the POST request. We map the basket items into a simplified format (ID and quantity) that the backend expects, then handle the response.&lt;br&gt;
JavaScript&lt;/p&gt;

&lt;p&gt;const placeOrder = async () =&amp;gt; {&lt;br&gt;
    if (!user) { navigate('/login'); return }&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const res = await fetch('/api/orders', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    customerID: user.customerID,
    items: basket.map(item =&amp;gt; ({
      productID: item.productID,
      quantity:  item.quantity
    }))
  })
})

const data = await res.json()
if (!res.ok) { setError(data.error); return }
setSuccess(true)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;}&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Conditional UI: Success vs. Checkout&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Instead of navigating to a brand new page, we use conditional rendering to swap the UI once the success state is true. This provides a fast, snappy experience for the customer.&lt;br&gt;
JavaScript&lt;/p&gt;

&lt;p&gt;if (success) {&lt;br&gt;
    return (&lt;br&gt;
      &lt;/p&gt;
&lt;br&gt;
        &lt;h1&gt;Order Placed!&lt;/h1&gt;
&lt;br&gt;
        &lt;p&gt;Thank you for your order.&lt;/p&gt;
&lt;br&gt;
         navigate('/orders')}&amp;gt;&lt;br&gt;
          View My Orders&lt;br&gt;
        &lt;br&gt;
      &lt;br&gt;
    )&lt;br&gt;
  }

&lt;ol&gt;
&lt;li&gt;Building the Review Section&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The main render provides a summary of the items and the total. Using .toFixed(2) ensures that our currency is always displayed with two decimal places, avoiding the "ugly" math results often found in JavaScript.&lt;br&gt;
JavaScript&lt;/p&gt;

&lt;p&gt;return (&lt;br&gt;
    &lt;/p&gt;
&lt;br&gt;
      &lt;h1&gt;Checkout&lt;/h1&gt;
&lt;br&gt;
      {basket.length === 0 ? (&lt;br&gt;
        &lt;p&gt;Your basket is empty.&lt;/p&gt;
&lt;br&gt;
      ) : (&lt;br&gt;
        &amp;lt;&amp;gt;&lt;br&gt;
          {basket.map((item, index) =&amp;gt; (&lt;br&gt;
            &lt;br&gt;
              &lt;p&gt;{item.product_name}&lt;/p&gt;
&lt;br&gt;
              &lt;p&gt;Quantity: {item.quantity}&lt;/p&gt;
&lt;br&gt;
              &lt;p&gt;£{(item.product_price * item.quantity).toFixed(2)}&lt;/p&gt;
&lt;br&gt;
            &lt;br&gt;
          ))}&lt;br&gt;
          &lt;br&gt;
            &lt;h2&gt;Total: £{total.toFixed(2)}&lt;/h2&gt;
&lt;br&gt;
          &lt;br&gt;
          {error &amp;amp;&amp;amp; &lt;p&gt;{error}&lt;/p&gt;}&lt;br&gt;
          &lt;br&gt;
            Place Order&lt;br&gt;
          &lt;br&gt;
        &amp;lt;/&amp;gt;&lt;br&gt;
      )}&lt;br&gt;
    &lt;br&gt;
  )&lt;br&gt;
}

&lt;ol&gt;
&lt;li&gt;Visual Polish and Feedback&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The CSS focuses on making the "Order" action prominent while providing clear error feedback if the API call fails.&lt;br&gt;
CSS&lt;/p&gt;

&lt;p&gt;/* Styling the success/action button &lt;em&gt;/&lt;br&gt;
.order-btn {&lt;br&gt;
  background: #A2C24A;&lt;br&gt;
  color: white;&lt;br&gt;
  border-radius: 6px;&lt;br&gt;
  padding: 12px 30px;&lt;br&gt;
  cursor: pointer;&lt;br&gt;
  font-family: 'Gluten', cursive; /&lt;/em&gt; Adding a bit of brand personality */&lt;br&gt;
}&lt;/p&gt;

&lt;p&gt;/* Highlighting the Total &lt;em&gt;/&lt;br&gt;
.checkout-total h2 {&lt;br&gt;
  color: #A2C24A; /&lt;/em&gt; Matching the button for visual harmony */&lt;br&gt;
}&lt;/p&gt;

&lt;p&gt;/* Clear Error Messaging */&lt;br&gt;
.error {&lt;br&gt;
  color: white;&lt;br&gt;
  background: red;&lt;br&gt;
  padding: 10px;&lt;br&gt;
  border-radius: 6px;&lt;br&gt;
}&lt;/p&gt;

&lt;p&gt;Key Takeaways&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Router State: Using location.state is a great way to pass "transient" data like a shopping basket between pages.

Safety First: Always check for a user object before allowing an order submission.

The Power of Reduce: reduce() is the cleanest way to handle mathematical sums in a React component.

Feedback Loops: Use clear success and error states to keep the user informed about what’s happening behind the scenes.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

</description>
      <category>javascript</category>
      <category>react</category>
      <category>tutorial</category>
      <category>webdev</category>
    </item>
  </channel>
</rss>
