Medusa.js is an open-source Shopify alternative built with JavaScript. It’s a great solution if you’re looking for a customizable ecommerce solution.
In this tutorial, we’ll see how to add Lob’s Address Autocomplete API feature to a Medusa project to give customers a faster checkout process.
Every bit of extra information that customers must enter in the checkout process adds to the likelihood of cart abandonment.
The information that takes the longest is undoubtedly the shipping address.
By using Lob’s Address Autocomplete API, we can not only save time for customers but also validate addresses to reduce the possibility of errors.
In this tutorial, we’ll add the Lob Autocomplete React component to the checkout form found in the Medusa Next.js starter template.
To begin, you’ll need to install these two repos:
You’ll find the installation instructions in the respective repos, so we won’t repeat them here for the sake of brevity.
After installing, make sure you run the dev server on both projects (which run on ports 8000 and 9000 by default).
This walkthrough is designed for a US Storefront—though Lob has International Autocomplete support and could be used for non-US storefronts. We will need to make three adjustments so you get US dollars (USD) instead of Euros displayed in our starter store.
In your Medusa store project, open my-medusa-store/data/seed.json
.
Swap the order of the regions, so NA (North America) is first, and therefore the default.
{
"id": "test-region-na",
"name": "NA",
"currency_code": "usd",
"tax_rate": 0,
"payment_providers": ["manual"],
"fulfillment_providers": ["manual"],
"countries": ["us", "ca"]
},
{
"id": "test-region-eu",
"name": "EU",
"currency_code": "eur",
"tax_rate": 0,
"payment_providers": ["manual"],
"fulfillment_providers": ["manual"],
"countries": ["gb", "de", "dk", "se", "fr", "es", "it"]
}
To seed your Medusa store run the following command:
medusa seed -f ./data/seed.json
The default currency of the Next template is set to EUR. Let’s change that to USD as Lob’s autocomplete API generates US addresses while in test mode.
To do this, go to the frontend project (all our customizations will apply to the frontend) and change the currencyCode
property to "usd"
in the file context/store-context.js
.
export const defaultStoreContext = {
...
currencyCode: "usd",
...
}
We’ll also need to change the default display from EUR to USD, which we can do in the file components/checkout/shipping-method.jsx
.
<p>{formatPrice(option.amount, "USD")}</p>
The default checkout page of the Medusa store currently looks like this:
Our approach to adding address autocomplete will be:
With this done, filling out the address form will be significantly faster—users only need to type a few characters to get a full address.
The main component for the checkout form is in the file components/checkout/information-step.jsx
. This is where you’ll find the address input that we’re going to replace with the address autocomplete component.
Let’s first make a copy of the input-field component and customize it to include the autocomplete. By doing it this way, we can keep the wrapper for error handling that is already present in the input-field.
To do this, go to the terminal and copy the input-field component to a new component address-field
:
$ cp components/checkout/input-field.jsx components/checkout/address-field.jsx
In the new component file, rename the exported function from InputField
to AddressField
.
const AddressField = () => { ... }
...
export default AddressField
We’re now going to install Lob’s React address autocomplete component, which provides a ready-to-use autocomplete, saving us from having to implement one from scratch.
Let’s first go to the terminal and install it with NPM. We add @previous
so the component supports React 17, which is compatible with the Medusa starter project.
$ npm i -S @lob/react-address-autocomplete@previous
To use the autocomplete API, we’ll need a Lob API key. The instructions for generating one are outlined here. Grab the publishable test key and add it to your .env.local
file. We’ll use a variable name NEXT_PUBLIC_LOB_PUBLISHABLE_KEY
. By prefixing it with NEXT_PUBLIC_
, we can make it accessible from the frontend source code.
javascript
NEXT_PUBLIC_LOB_PUBLISHABLE_KEY=<your publishable key>
Now, open the component file components/checkout/address-field.jsx
and import the Autocomplete component at the top of the file under the existing imports.
Then, create a variable apiKey
and assign to it your publishable Lob API key.
import { Autocomplete } from "@lob/react-address-autocomplete";
const apiKey = process.env.NEXT_PUBLIC_LOB_PUBLISHABLE_KEY;
We’ll then replace the Field
component declaration here with the autocomplete component. To do that, locate where Field
is declared in the JSX template.
<Field
id={id}
name={id}
placeholder={placeholder}
className={styles.styledfield}
type={type}
disabled={disabled}
/>
Replace that with the following:
<Autocomplete
id={id}
apiKey={apiKey}
/>
Let’s now add our newly created address field to the checkout form. The first thing we’ll do is import the component at the top of the file components/checkout/information-step.jsx
.
import AddressField from "./address-field";
Next, in the same file, we’ll replace the address line 1 InputField
component. You can identify it by the id address_1
.
<InputField
id="address_1"
placeholder="Address 1"
error={errors.address_1 && touched.address_1}
errorMsg={errors.address_1}
type="text"
/>
Replace that with the following:
<AddressField
id="address_1"
error={errors.address_1 && touched.address_1}
errorMsg={errors.address_1}
/>
Note that we keep the id
, error
, and errorMsg
props but not the others as they aren’t relevant in our new address field.
To make the Lob autocomplete component fit with the Medusa checkout’s appearance, we’ll need to add a few CSS rules.
Let’s create a CSS module in the styles directory called address.module.css
.
$ touch styles/address.module.css
Put the following rules into that new file:
css
.address {
width: 100%;
}
.address :active {
outline: none;
border: none;
}
.address > div {
font-size: 13.3px;
border-style: none;
border-radius: 8px;
}
Let’s now import the CSS module file into our address field component and apply the class addressStyle.address
to the autocomplete component.
import addressStyle from '../../styles/address.module.css';
<Autocomplete
id={id}
apiKey={apiKey}
className={addressStyle.address}
/>
With that done, the autocomplete component has now been integrated into the checkout. Go ahead and start typing into the form and you should see the address autocomplete working.
As mentioned, the checkout form doesn’t include a field for the customer’s state by default. We’ll need this field as it’s required for US shipping addresses.
Although not in the form, the Medusa checkout API does include a field “province” which we should use for the state.
We’ll first create the field in the form’s validation schema, which is handled by the library Yup. You’ll see the config object already has a value province
that is set to be nullable. Let’s change this so it’s now required by removing nullable()
and replacing it with required("Required")
.
let Schema = Yup.object().shape({
...
province: Yup.string().required("Required"),
})
The checkout also uses the form library Formik. This has a prop initialValues
which allows the form to be pre-filled with saved values for returning customers.
Let’s add the province field to the initialValues
by adding a property province
and assigning to it savedValues.province || ""
<Formik
initialValues={{
...
province: savedValues.province || "",
}}
>
We’ll now add the state (i.e., province) field to the form. We’ll make it share a row with the country field so the form is not too long.
To do this, find the field with id country_code
. Wrap this in a div with className={styles.sharedrow}
.
Add an InputField
child with id province
. You can make the placeholder “State” so it’s consistent with US addresses.
<div className={styles.sharedrow}>
<SelectField
id="country_code"
options={cart.region?.countries}
error={errors.country_code && touched.country_code}
errorMsg={errors.country_code}
/>
<InputField
id="province"
placeholder="State"
error={errors.province && touched.province}
errorMsg={errors.province}
type="text"
/>
</div>
With that done, the form will now have a state field next to the country field.
Looking again at the address autocomplete field, you’ll see that as we begin typing an address, we get suggestions in a drop-down below.
When the user selects one of these suggestions, the autocomplete component calls an onSelection
callback and passes the selected address to it.
Let’s create a handler function handleSelect
and pass it to the onSelection
prop. In this function, we’ll take the selected autocomplete address and populate the other address fields in the form.
The first thing we need to do is import the useFormikContext
hook from the formik module. This gives us access to the form API in a functional component.
Let’s then destructure the object to get the setFieldValue
method.
import { useFormikContext } from "formik";
const AddressField = ({ id, error, errorMsg }) => {
const { setFieldValue } = useFormikContext();
...
}
Now we can define the callback function. This function will receive an object argument that has a property. This is itself an object with the full address components as sub properties.
Let’s now use the setFieldValue
method in the callback to provide values for postal_code
, city
, province
, address_1
, and country_code
.
const handleSelect = ({ value }) => {
setFieldValue('postal_code', value.zip_code);
setFieldValue('city', value.city);
setFieldValue('province', value.state.toUpperCase());
setFieldValue('address_1', value.primary_line);
setFieldValue('country_code', 'US');
}
Finally, we’ll assign handleSelect
to the onSelection
prop. We’ll also add the primaryLineOnly
prop. What this does is ensure that when the user makes a selection, the entire address (with state, country, zip code, etc.) is not populated in the address field—just the primary line is, e.g., “12 Test St”.
<Autocomplete
id={id}
apiKey={apiKey}
className={addressStyle.address}
onSelection={handleSelect}
primaryLineOnly
/>
With that done, we now have implemented a quick and stylish address autocomplete in Medusa! By typing a few characters into the autocomplete, customers get their validated address pre-filled, saving them a significant amount of time.