Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions .changeset/upgrade-nextjs-16.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
---
"@bigcommerce/catalyst-core": minor
---

Upgrade Next.js to v16 and align peer dependencies.

To migrate your Catalyst storefront to Next.js 16:

- Update `next` to `^16.0.0` in your `package.json` and install dependencies.
- Replace any usage of `unstable_expireTag` with `revalidateTag` and `unstable_expirePath` with `revalidatePath` from `next/cache`.
- Update `tsconfig.json` to use `"moduleResolution": "bundler"` and `"module": "nodenext"` as required by Next.js 16.
- Address Next.js 16 deprecation lint errors (e.g. legacy `<img>` elements, missing `rel="noopener noreferrer"` on external links).
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { BigCommerceAPIError, BigCommerceGQLError } from '@bigcommerce/catalyst-client';
import { parseWithZod } from '@conform-to/zod';
import { unstable_expireTag as expireTag } from 'next/cache';
import { revalidateTag } from 'next/cache';
import { getTranslations } from 'next-intl/server';
import { z } from 'zod';

Expand Down Expand Up @@ -234,7 +234,7 @@ export async function createAddress(
};
}

expireTag(TAGS.customer);
revalidateTag(TAGS.customer, { expire: 0 });

return {
addresses: [
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { BigCommerceGQLError } from '@bigcommerce/catalyst-client';
import { parseWithZod } from '@conform-to/zod';
import { unstable_expireTag as expireTag } from 'next/cache';
import { revalidateTag } from 'next/cache';
import { getTranslations } from 'next-intl/server';
import { z } from 'zod';

Expand Down Expand Up @@ -78,7 +78,7 @@ export async function deleteAddress(prevState: Awaited<State>, formData: FormDat
};
}

expireTag(TAGS.customer);
revalidateTag(TAGS.customer, { expire: 0 });

return {
addresses: prevState.addresses.filter(
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { BigCommerceGQLError } from '@bigcommerce/catalyst-client';
import { parseWithZod } from '@conform-to/zod';
import { unstable_expireTag as expireTag } from 'next/cache';
import { revalidateTag } from 'next/cache';
import { getTranslations } from 'next-intl/server';
import { z } from 'zod';

Expand Down Expand Up @@ -247,7 +247,7 @@ export async function updateAddress(
};
}

expireTag(TAGS.customer);
revalidateTag(TAGS.customer, { expire: 0 });

return {
addresses: prevState.addresses.map((address) =>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

import { BigCommerceGQLError } from '@bigcommerce/catalyst-client';
import { parseWithZod } from '@conform-to/zod';
import { unstable_expireTag } from 'next/cache';
import { revalidateTag } from 'next/cache';
import { getTranslations } from 'next-intl/server';

import { updateAccountSchema } from '@/vibes/soul/sections/account-settings/schema';
Expand Down Expand Up @@ -75,7 +75,7 @@ export const updateCustomer: UpdateAccountAction = async (prevState, formData) =
};
}

unstable_expireTag(TAGS.customer);
revalidateTag(TAGS.customer, { expire: 0 });

return {
account: submission.value,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import { BigCommerceGQLError } from '@bigcommerce/catalyst-client';
import { SubmissionResult } from '@conform-to/react';
import { parseWithZod } from '@conform-to/zod';
import { unstable_expireTag } from 'next/cache';
import { revalidateTag } from 'next/cache';
import { getTranslations } from 'next-intl/server';
import { z } from 'zod';

Expand Down Expand Up @@ -120,7 +120,7 @@ export const updateNewsletterSubscription = async (
};
}

unstable_expireTag(TAGS.customer);
revalidateTag(TAGS.customer, { expire: 0 });

return {
lastResult: submission.reply(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ export async function toggleWishlistVisibility(
};
}

revalidateTag(TAGS.customer);
revalidateTag(TAGS.customer, { expire: 0 });

return {
lastResult: submission.reply(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ export async function deleteWishlist(
};
}

revalidateTag(TAGS.customer);
revalidateTag(TAGS.customer, { expire: 0 });

// Server toast has to be used here since the item is being deleted. When revalidateTag is called,
// the wishlist items will update, and the element node containing the useEffect will be removed.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ export async function newWishlist(prevState: Awaited<State>, formData: FormData)
};
}

revalidateTag(TAGS.customer);
revalidateTag(TAGS.customer, { expire: 0 });

return {
lastResult: submission.reply(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ export async function removeWishlistItem(
};
}

revalidateTag(TAGS.customer);
revalidateTag(TAGS.customer, { expire: 0 });

// Server toast has to be used here since the item is being deleted. When revalidateTag is called,
// the wishlist items will update, and the element node containing the useEffect will be removed.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ export async function renameWishlist(
};
}

revalidateTag(TAGS.customer);
revalidateTag(TAGS.customer, { expire: 0 });

return {
lastResult: submission.reply(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ export const addShippingCost = async ({

const result = response.data.checkout.selectCheckoutShippingOption?.checkout;

revalidateTag(TAGS.checkout);
revalidateTag(TAGS.checkout, { expire: 0 });

return result;
};
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ export const addCheckoutShippingConsignments = async ({
fetchOptions: { cache: 'no-store' },
});

revalidateTag(TAGS.checkout);
revalidateTag(TAGS.checkout, { expire: 0 });

return response.data.checkout.addCheckoutShippingConsignments?.checkout;
};
Expand Down Expand Up @@ -135,7 +135,7 @@ export const updateCheckoutShippingConsignment = async ({
fetchOptions: { cache: 'no-store' },
});

revalidateTag(TAGS.checkout);
revalidateTag(TAGS.checkout, { expire: 0 });

return response.data.checkout.updateCheckoutShippingConsignment?.checkout;
};
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ export const applyCouponCode = async ({ checkoutEntityId, couponCode }: Props) =

const checkout = response.data.checkout.applyCheckoutCoupon?.checkout;

revalidateTag(TAGS.checkout);
revalidateTag(TAGS.checkout, { expire: 0 });

return checkout;
};
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ export const applyGiftCertificate = async ({ checkoutEntityId, giftCertificateCo

const checkout = response.data.checkout.applyCheckoutGiftCertificate?.checkout;

revalidateTag(TAGS.checkout);
revalidateTag(TAGS.checkout, { expire: 0 });

return checkout;
};
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ export const removeCouponCode = async ({ checkoutEntityId, couponCode }: Props)

const checkout = response.data.checkout.unapplyCheckoutCoupon?.checkout;

revalidateTag(TAGS.checkout);
revalidateTag(TAGS.checkout, { expire: 0 });

return checkout;
};
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ export const removeGiftCertificate = async ({ checkoutEntityId, giftCertificateC

const checkout = response.data.checkout.unapplyCheckoutGiftCertificate?.checkout;

revalidateTag(TAGS.checkout);
revalidateTag(TAGS.checkout, { expire: 0 });

return checkout;
};
4 changes: 2 additions & 2 deletions core/app/[locale]/(default)/cart/_actions/remove-item.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
'use server';

import { unstable_expireTag } from 'next/cache';
import { revalidateTag } from 'next/cache';
import { getTranslations } from 'next-intl/server';

import { getSessionCustomerAccessToken } from '~/auth';
Expand Down Expand Up @@ -62,7 +62,7 @@ export async function removeItem({
await clearCartId();
}

unstable_expireTag(TAGS.cart);
revalidateTag(TAGS.cart, { expire: 0 });

return cart;
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
'use server';

import { unstable_expirePath } from 'next/cache';
import { revalidatePath } from 'next/cache';
import { getTranslations } from 'next-intl/server';

import { getSessionCustomerAccessToken } from '~/auth';
Expand Down Expand Up @@ -87,7 +87,7 @@ export const updateQuantity = async ({
throw new Error(t('failedToUpdateQuantity'));
}

unstable_expirePath('/cart');
revalidatePath('/cart');

return cart;
};
Original file line number Diff line number Diff line change
Expand Up @@ -235,7 +235,7 @@ export async function wishlistAction(payload: FormData): Promise<void> {
}
}

revalidateTag(TAGS.customer);
revalidateTag(TAGS.customer, { expire: 0 });
} catch (error) {
// eslint-disable-next-line no-console
console.error(error);
Expand Down
2 changes: 1 addition & 1 deletion core/components/header/_actions/switch-currency.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ export const switchCurrency = async (_prevState: SubmissionResult | null, payloa
if (cartId) {
await updateCartCurrency(cartId, submission.value.id)
.then(() => {
revalidateTag(TAGS.cart);
revalidateTag(TAGS.cart, { expire: 0 });
})
.catch((error: unknown) => {
// eslint-disable-next-line no-console
Expand Down
6 changes: 3 additions & 3 deletions core/lib/cart/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
'use server';

import { unstable_expireTag } from 'next/cache';
import { revalidateTag } from 'next/cache';

import { auth, getAnonymousSession, updateAnonymousSession, updateSession } from '~/auth';
import { TAGS } from '~/client/tags';
Expand Down Expand Up @@ -59,7 +59,7 @@ export async function addToOrCreateCart(
throw new MissingCartError();
}

unstable_expireTag(TAGS.cart);
revalidateTag(TAGS.cart, { expire: 0 });

return;
}
Expand All @@ -72,5 +72,5 @@ export async function addToOrCreateCart(

await setCartId(createResponse.data.cart.createCart.cart.entityId);

unstable_expireTag(TAGS.cart);
revalidateTag(TAGS.cart, { expire: 0 });
}
6 changes: 3 additions & 3 deletions core/middlewares/compose-middlewares.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import { type NextMiddleware, NextResponse } from 'next/server';
import { type NextProxy, NextResponse } from 'next/server';

export type MiddlewareFactory = (middleware: NextMiddleware) => NextMiddleware;
export type MiddlewareFactory = (middleware: NextProxy) => NextProxy;

export const composeMiddlewares = (
firstMiddlewareWrapper: MiddlewareFactory,
...otherMiddlewareWrappers: MiddlewareFactory[]
): NextMiddleware => {
): NextProxy => {
const middlewares = otherMiddlewareWrappers.reduce(
(accumulatedMiddlewares, nextMiddleware) => (middleware) =>
accumulatedMiddlewares(nextMiddleware(middleware)),
Expand Down
6 changes: 3 additions & 3 deletions core/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -57,9 +57,9 @@
"lodash.debounce": "^4.0.8",
"lru-cache": "^11.1.0",
"lucide-react": "^0.474.0",
"next": "15.5.10",
"next": "^16.1.6",
"next-auth": "5.0.0-beta.30",
"next-intl": "^4.1.0",
"next-intl": "^4.6.1",
"nuqs": "^2.4.3",
"p-lazy": "^5.0.0",
"react": "19.1.5",
Expand All @@ -79,7 +79,7 @@
"@bigcommerce/eslint-config-catalyst": "workspace:^",
"@faker-js/faker": "^9.8.0",
"@gql.tada/cli-utils": "^1.6.3",
"@next/bundle-analyzer": "15.5.10",
"@next/bundle-analyzer": "^16.1.6",
"@playwright/test": "^1.52.0",
"@tailwindcss/container-queries": "^0.1.1",
"@tailwindcss/typography": "^0.5.16",
Expand Down
3 changes: 2 additions & 1 deletion core/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
"moduleResolution": "bundler",
"resolveJsonModule": true,
"isolatedModules": true,
"jsx": "preserve",
"jsx": "react-jsx",
"incremental": true,
"plugins": [
{
Expand Down Expand Up @@ -48,6 +48,7 @@
"**/*.ts",
"**/*.tsx",
".next/types/**/*.ts",
".next/dev/types/**/*.ts",
"tests/**/*"
],
"exclude": ["node_modules", ".next"]
Expand Down
2 changes: 1 addition & 1 deletion core/vibes/soul/primitives/product-card/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ export function ProductCard({
}[colorScheme],
)}
fill
priority={imagePriority}
preload={imagePriority}
sizes={imageSizes}
src={image.src}
/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,4 @@ import { revalidateTag } from 'next/cache';
import { TAGS } from '~/client/tags';

// eslint-disable-next-line @typescript-eslint/require-await
export const revalidateCart = async () => revalidateTag(TAGS.cart);
export const revalidateCart = async () => revalidateTag(TAGS.cart, { expire: 0 });
Original file line number Diff line number Diff line change
Expand Up @@ -269,7 +269,7 @@ export function ProductGallery({
}[fit],
)}
fill
priority={idx === 0}
preload={idx === 0}
sizes="(min-width: 42rem) 50vw, 100vw"
src={image.src}
/>
Expand Down
2 changes: 1 addition & 1 deletion core/vibes/soul/sections/slideshow/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -199,7 +199,7 @@ export function Slideshow({ slides, playOnInit = true, interval = 5000, classNam
placeholder={
image.blurDataUrl != null && image.blurDataUrl !== '' ? 'blur' : 'empty'
}
priority={idx === 0}
preload={idx === 0}
sizes="100vw"
src={image.src}
/>
Expand Down
Loading
Loading