Skip to main content
Version: 3.x

3.17 -> 3.18

This page lists the highlights for upgrading a project from Front-Commerce 3.17 to 3.18

Update dependencies

Update all your @front-commerce/* dependencies to this version:

pnpm update "@front-commerce/*@3.18.0"

Manual Migration

Deprecation of FRONT_COMMERCE_SERVER_DISABLE_TRAILING_SLASH_REDIRECT configuration

In this version, we introduced a new configuration to be able to either force trailing slashes or force the removal them.

This new configuration replaces the old FRONT_COMMERCE_SERVER_DISABLE_TRAILING_SLASH_REDIRECT configuration, which will be removed in the next major version.

If you are using the old configuration, we encourage you to update it to use the new one instead:

export default defineConfig({
server: {
// Equivalent to FRONT_COMMERCE_SERVER_DISABLE_TRAILING_SLASH_REDIRECT=true
// Default is "remove"
trailingSlash: "ignore",
},
});

Or using the environment variable:

- FRONT_COMMERCE_SERVER_DISABLE_TRAILING_SLASH_REDIRECT=true
+ FRONT_COMMERCE_SERVER_TRAILING_SLASH=ignore

React 19 compatibility

In this version, we have made sure that Front-Commerce is fully compatible with React 19.

With this upgrade, we have removed the support for react-test-renderer. If you are using it, you need to use @testing-library/react instead and update your tests.

Leaflet 1.9 and react-leaflet 5 compatibility

In this version, we have made sure that Leaflet 1.9 and react-leaflet 5 are fully compatible.

With this upgrade, we have modified the Map.client.tsx component to add a style prop to the MapContainer component. If you have overridden the Map component from the @front-commerce/leaflet package, you need to update your code to add the style prop.

app/theme/Leaflet/Map.client.tsx
import { MapContainer } from "react-leaflet";

const Map = ({
locations,
getMarker,
zoom = 12,
defaultBounds = [],
defaultCenter,
onBoundsChanged,
}: MapProps) => {
// ...
return (
<MapContainer
className="map"
center={centerCoordinates}
zoom={zoom}
style={{ height: "100%", width: "100%" }}
>
{/* ... */}
</MapContainer>
);
};

@adyen/adyen-web 6 and @adyen/api-library 30 compatibility

In this version, we have made sure that @adyen/adyen-web 6 and @adyen/api-library 30 are fully compatible.

To keep up with the latest Adyen package updates, we have modified:

  • how styles are imported from @adyen/adyen-web
  • how the AdyenCheckout method is imported from @adyen/adyen-web
  • how the Dropin component is created
  • the checkout configuration, which now includes the country code

Style import

If you have overridden any of the following files, you need to update your code to use the new import path:

  • theme/modules/Adyen/PaypalCheckout/withDropinMounter.jsx
  • theme/modules/Adyen/AdyenPaymentAction.jsx
  • theme/modules/Checkout/Payment/AdditionalPaymentInformation/AdyenCheckout/AdyenCheckout.jsx
  • theme/modules/Checkout/Payment/AdditionalPaymentInformation/AdyenCheckout/AdyenCheckoutError.jsx
- import "@adyen/adyen-web/dist/adyen.css";
+ import "@adyen/adyen-web/styles/adyen.css";

AdyenCheckout and Dropin components

theme/modules/Adyen/withAdyenCheckout.tsx
import type { FunctionComponent } from "react";
import { Suspense, lazy } from "react";
import LoadingArea from "theme/components/molecules/LoadingArea";
+ import type { DropinConfiguration, ICore } from "@adyen/adyen-web";

function enhanceComponentWithAdyenCheckout(
EnhancedComponent: FunctionComponent,
) {
return lazy(async () => {
- const { default: adyenCheckout } = await import("@adyen/adyen-web");
+ const {
+ AdyenCheckout: adyenCheckout,
+ Dropin,
+ Card,
+ PayPal,
+ SepaDirectDebit,
+ FacilPay3x,
+ FacilPay4x,
+ GooglePay,
+ Klarna,
+ } = await import("@adyen/adyen-web");
+
+ type DropinVariant = "paypal" | "default";
+
+ const getAdyenDropin = (
+ checkout: ICore,
+ variant: DropinVariant = "default",
+ ) => {
+ let paymentMethodComponents: DropinConfiguration["paymentMethodComponents"];
+ switch (variant) {
+ case "paypal":
+ paymentMethodComponents = [PayPal];
+ break;
+ case "default":
+ paymentMethodComponents = [
+ Card,
+ SepaDirectDebit,
+ FacilPay3x,
+ FacilPay4x,
+ GooglePay,
+ Klarna,
+ ];
+ break;
+ }
+ return new Dropin(checkout, { paymentMethodComponents });
+ };
+
return {
default: (props) => (
- <EnhancedComponent {...props} adyenCheckout={adyenCheckout} />
+ <EnhancedComponent
+ {...props}
+ adyenCheckout={adyenCheckout}
+ getAdyenDropin={getAdyenDropin}
+ />
),
};
});
}

If you use additional payment methods, you may need to import extra components from @adyen/adyen-web and add them to the getAdyenDropin function. Please refer to the Adyen documentation for the list of available payment methods.

theme/modules/Checkout/Payment/AdditionalPaymentInformation/AdyenCheckout/AdyenCheckout.jsx
- const AdyenCheckout = ({ onAuthorize, gscAccepted, method, adyenCheckout }) => {
+ const AdyenCheckout = ({
+ onAuthorize,
+ gscAccepted,
+ method,
+ adyenCheckout,
+ getAdyenDropin
+ }) => {
  useEffect(() => {
// ...
adyenCheckout(configuration).then((checkout) => {
- setComponent(checkout.create("dropin"));
+ setComponent(getAdyenDropin(checkout, "default"));
});
}, [onSubmit, data, adyenCheckout]);
theme/modules/Adyen/PaypalCheckout/withDropinMounter.jsx
const {
paymentAdditionalData,
totalInclTax,
onSubmit,
onAdditionalDetails,
adyenCheckout,
mutable,
+ getAdyenDropin
} = props;
  adyenCheckout(configuration).then((checkout) => {
- const dropin = checkout.create("dropin");
+ const dropin = getAdyenDropin(checkout, "paypal");
setComponent(dropin);
});

Country code

  1. theme/modules/Adyen/AdyenPaymentAction.jsx
const configuration = {
...dropinConfig,
environment: dropinConfig.isDemoMode ? "test" : "live",
+ countryCode: dropinConfig.locale.split("-")[1],
// ...
};
  1. theme/modules/Checkout/Payment/AdditionalPaymentInformation/AdyenCheckout/AdyenCheckout.jsx
const configuration = {
...getPaymentMethodsConfiguration(method),
...adyenData?.publicConfig,
environment: adyenData?.publicConfig?.isDemoMode ? "test" : "live",
+ countryCode: adyenData.paymentMethod.countryCode,
// ...
};

Restock information (Gezy)

In this version, we have added a new field to the product stock information: restock.

This field will be use to display the restock information in the PLP and the PDP.

note

If you are using Magento, restock will return nothing. Then, the next changes are not required for you.

If you have overridden one of the following files, you need to update your code :

  • theme/modules/AddToCart/AddToCart.jsx
  • theme/modules/AddToCart/AddToCart.scss
  • theme/modules/AddToCart/ProductStockFragment.gql
  • theme/modules/ProductView/ProductItem/Overview/Overview.jsx
  • theme/modules/ProductView/ProductItem/Overview/Overview.scss
theme/modules/AddToCart/AddToCart.jsx
// Other imports...
import ProductPackagingPicker from "theme/components/organisms/ProductPackagingPicker/ProductPackagingPicker";
+ import RestockInfo from "theme/modules/AddToCart/RestockInfo";
if (!product?.stock?.isInStock) {
+ if (product?.stock?.restock) {
+ return <RestockInfo restock={product.stock.restock} sku={product?.sku} />;
+ }
return (
<OutOfStock
message={message}
unavailableLabel={unavailableLabel}
sku={product?.sku}
/>
);
}
theme/modules/AddToCart/AddToCart.scss
.add-to-cart {
// Other styles...

+ &__restock-info {
+ font-size: var(--largeSize);
+ color: var(--info);
+ }
}
theme/modules/AddToCart/ProductStockFragment.gql
+ #import "theme/modules/AddToCart/RestockInfoFragment.gql"
+
fragment ProductStockFragment on Product {
sku
stock {
isInStock
+ restock {
+ ...RestockInfoFragment
+ }
}
// Other fields...
}
theme/modules/ProductView/ProductItem/Overview/Overview.jsx
<div
className="product-overview__price"
itemProp="offers"
itemScope
itemType="http://schema.org/Offer"
>
<meta itemProp="url" content={seoUrl} />
{product.stock.isInStock ? (
<ProductPrice prices={product.prices} priceSuffix={priceSuffix} />
+ ) : product.stock.restock ? (
+ <div className="product-overview__in-restock">
+ <FormattedMessage
+ id="modules.ProductView.Overview.inRestock"
+ defaultMessage="In restock"
+ />
+ </div>
) : (
<div className="product-overview__sold-out">
<FormattedMessage
id="modules.ProductView.Overview.outOfStock"
defaultMessage="Out of stock"
/>
</div>
)}
</div>
theme/modules/ProductView/ProductItem/Overview/Overview.scss
.product-overview {
// Other styles...

+ &__in-restock {
+ color: var(--info);
+ font-size: var(--regularSize);
+ min-height: var(--giantSize);
+ }
}

New loopbackUrl Store Configuration

A new optional loopbackUrl configuration has been added to the store configuration. This is useful in containerized environments where the public URL is not accessible from within the container.

If you're running Front-Commerce in Docker or Kubernetes and experiencing issues with SSR GraphQL requests failing because the public domain is not resolvable internally, you can now configure an internal URL:

app/config/stores.js
export default {
default: {
locale: "en-GB",
currency: "EUR",
url: "https://mysite.dev",
loopbackUrl: process.env.FRONT_COMMERCE_LOOPBACK_URL || null,
},
};

See the Configure multiple stores guide for more details.