` containing a static string, “Hello World”.
+
+## Registering the component with Makeswift
+
+Now that we have our component, we need to register it with Makeswift so that it can be used in the editor.
+
+Let's create a new folder inside the existing directory, `core/lib/makeswift/components`:
+
+```bash copy
+mkdir core/lib/makeswift/components/hello-world
+```
+
+Inside of the folder let's create a new file called `hello-world.makeswift.tsx`:
+
+```bash copy
+touch core/lib/makeswift/components/hello-world/hello-world.makeswift.tsx
+```
+
+Now, let's add the following code to the `hello-world.makeswift.tsx` file:
+
+```tsx filename="core/lib/makeswift/components/hello-world/hello-world.makeswift.tsx" copy
+import { runtime } from "~/lib/makeswift/runtime";
+
+import { HelloWorld } from "~/components/hello-world";
+
+runtime.registerComponent(HelloWorld, {
+ type: "hello-world",
+ label: "Basic / Hello World",
+});
+```
+
+Here, we'll use the `registerComponent` method from the Makeswift runtime, which takes two parameters; a valid React component and a configuration object with properties for `type`, `label`, `icon`, `hidden`, and `props`. For now, we've defined just `type` and `label`. You can read more about the properties in the [Makeswift documentation](https://docs.makeswift.com/developer/reference/runtime/register-component#arguments).
+
+Next, let's go ahead and import our new Makeswift registration file in `core/lib/makeswift/components.ts`. It's important to know that any new component needs to be imported into this file for it to be available in the Makeswift.
+
+```ts {5} filename="core/lib/makeswift/components.ts"
+// ...
+import "./components/slideshow/slideshow.makeswift";
+import "./components/sticky-sidebar/sticky-sidebar.makeswift";
+import "./components/product-detail/register";
+import "./components/hello-world/hello-world.makeswift";
+```
+
+## Running the development server and viewing in Makeswift
+
+Now that we've registered our component, let's run the dev server and view it in the Makeswift editor.
+
+```bash copy
+pnpm run dev
+```
+
+This will run the Catalyst storefront at `localhost:3000` by default, but we instead want to see the new component in Makeswift. To do this, open your Makeswift development site which should be connected to your locally running code. For more information on how to find your Makeswift development site URL, see the [Running Locally](/docs/storefront/catalyst/getting-started/local-development) guide.
+
+Once in Makeswift, you should see your new component in the sidebar.
+
+
+
+## Props and controls
+
+Before extending this `HelloWorld` component example to become our new product carousel component, I want to touch on two important concepts: Props and Controls.
+
+As we know, React components can take props. For example, inside of the hello world component, `core/components/hello-world/index.tsx`, let's add a name property which is a string:
+
+```tsx {1,2} filename="core/components/hello-world/index.tsx" copy
+export function HelloWorld({ name }: { name: string }) {
+ return
Hello {name}
;
+}
+```
+
+Since our new prop `name` is required, you'll notice in the component registration file that Typescript is complaining that the `name` prop has not been defined as a Makeswift control. This is where the `props` property of the second parameter of the `registerComponent` method comes into play. I can go ahead and add a `TextInput` control to the `name` prop:
+
+```tsx {1,10-15} filename="core/lib/makeswift/components/hello-world/hello-world.makeswift.tsx" copy
+import { TextInput } from "@makeswift/runtime/controls";
+
+import { runtime } from "~/lib/makeswift/runtime";
+
+import { HelloWorld } from "~/components/hello-world";
+
+runtime.registerComponent(HelloWorld, {
+ type: "hello-world",
+ label: "Basic / Hello World",
+ props: {
+ name: TextInput({
+ label: "Name",
+ defaultValue: "World",
+ }),
+ },
+});
+```
+
+The `registerComponent` method uses some Typescript magic to map the props defined in the `props` object to the props taken as parameters in the React component. Every time you add a new Makeswift control, you need to make sure that an appropriately named property exists in the registered React component.
+
+Now, if we reload our Makeswift editor, you'll notice that you can click on the component and edit the name prop in your Makeswift editor. This is the underlying strength of Makeswift; users can make easy changes to your website, using whatever prop interfaces you have exposed to them.
+
+
+
+## Creating the `MyProductCarousel` component
+
+Next up, let's follow a similar process to create a `MyProductCarousel` component. Instead of building the primitive Carousel UI component from scratch, we'll leverage the [`ProductCarousel` component in VIBES](https://vibes.site/docs/soul/product-carousel). This component is already included for you in the Catalyst source code.
+
+First, we'll want to create a new Makeswift component registration file inside of `core/lib/makeswift/components`. We'll first create the folder to hold the file, called `my-product-carousel/`:
+
+```bash copy
+mkdir core/lib/makeswift/components/my-product-carousel
+```
+
+Then the file we create inside that folder will be called `my-product-carousel.makeswift.tsx`:
+
+```bash copy
+touch core/lib/makeswift/components/my-product-carousel/my-product-carousel.makeswift.tsx
+```
+
+## Creating the wrapper component
+
+Next, let's go ahead and import both the `ProductCarousel` and `ProductsCarouselSkeleton` components from `@/vibes/soul/sections/products-carousel`. This is the primitive component that comes from VIBES.
+
+Remember that our `HelloWorld` component accepted a single prop called `name`. Notice that the `ProductCarousel` component from VIBES is already configured to take a number of props, including `products` which is required.
+
+Next, let's get the `MyProductCarousel` component to render inside of my Makeswift editor, similar to what we did with our `HelloWorld` component.
+
+For this, I'll start by importing `runtime` from `~/lib/makeswift/runtime`.
+
+Next, we'll create a wrapper component. We can call this wrapper something along the lines of `MSMyProductCarousel`. The purpose of this wrapper is to fetch product data, display a skeleton loader while the component fetches data, validate the data that is fetched, and then if possible, pass the returned product data to the VIBES `ProductCarousel` component.
+
+```tsx filename="core/lib/makeswift/components/my-product-carousel/my-product-carousel.makeswift.tsx" copy
+import { ProductCarousel, ProductsCarouselSkeleton } from '@/vibes/soul/sections/product-carousel';
+import { runtime } from '~/lib/makeswift/runtime';
+
+import { useProducts } from '../../utils/use-products';
+
+runtime.registerComponent(
+ function MSMyProductCarousel() {
+ const { products, isLoading } = useProducts({
+ collection: 'featured',
+ additionalProductIds: [],
+ });
+
+ if (isLoading) {
+ return
;
+ }
+
+ if (products == null || products.length === 0) {
+ return
;
+ }
+
+ return
;
+ },
+ {
+ type: 'my-product-carousel',
+ label: 'Catalog / My Product Carousel',
+ },
+);
+```
+
+Our wrapper component doesn't take props yet, but we'll add that shortly. Notice we have hard-coded values assigned for `collection` and `additionalProductIds`. In the future, anytime you're implementing a component that takes props, you should think about whether or not those component props should be controllable inside Makeswift. Later, we'll create Makeswift controls for `collection` and `additionalProductIds` so that these can be controlled and edited visually without adjusting code.
+
+Note that we pulled in the `useProducts` hook to fetch product data. If you look at the source code of that hook, you'll notice that it's a client-side Fetch making a request to a proxy API endpoint `/api/products/group/${collection}`. The reason we need to make a request to a proxy API endpoint is because this hook will run only in the browser, and since the BigCommerce GraphQL Storefront API is a server-to-server API, we can't call it directly from the client. We must create an API endpoint that our client can proxy requests through. If you want your client-side Makeswift component to render data from your BigCommerce storefront, you'll need to make sure you create a proxy API endpoint for the data you want to fetch.
+
+Next, let's go ahead and import our new Makeswift registration file in `core/lib/makeswift/components.ts`, right under the import for our `hello-world.makeswift` component.
+
+```ts {3} filename="core/lib/makeswift/components.ts"
+// ...
+import './components/hello-world/hello-world.makeswift';
+import './components/my-product-carousel/my-product-carousel.makeswift';
+```
+
+## Previewing in Makeswift
+
+Great! Back in Makeswift, drag out a box right above the “Shop by category” section, and then, from the component tray, drag out the `MyProductCarousel` component inside of that box.
+
+
+
+
+ If you do not see any products in the carousel, it is because the `featured`
+ collection is empty. Mark some products as featured in your BigCommerce
+ channel settings, or try changing the `collection` prop to `best-selling` or
+ `newest`.
+
+
+## Adding the style control
+
+Up next, let's add our first Makeswift control to the component, the `Style()` control.
+
+```tsx {1,23,27,30,35-37} filename="core/lib/makeswift/components/my-product-carousel/my-product-carousel.makeswift.tsx" copy
+import { Style } from '@makeswift/runtime/controls';
+
+import { ProductCarousel, ProductsCarouselSkeleton } from '@/vibes/soul/sections/product-carousel';
+import { runtime } from '~/lib/makeswift/runtime';
+
+import { useProducts } from '../../utils/use-products';
+
+interface MSMyProductCarouselProps {
+ className: string;
+}
+
+runtime.registerComponent(
+ function MSMyProductCarousel({ className }: MSMyProductCarouselProps) {
+ const { products, isLoading } = useProducts({
+ collection: 'featured',
+ additionalProductIds: [],
+ });
+
+ if (isLoading) {
+ return
;
+ }
+
+ if (products == null || products.length === 0) {
+ return
;
+ }
+
+ return
;
+ },
+ {
+ type: 'my-product-carousel',
+ label: 'Catalog / My Product Carousel',
+ props: {
+ className: Style(),
+ },
+ },
+);
+```
+
+Note we're also adding the `MSMyProductCarouselProps` interface to define props which includes `className`.
+
+If you reload your Makeswift editor, you'll notice that you can now edit the `className` prop by clicking on the component and then adjusting the width, height, and other properties in the right sidebar:
+
+
+
+## Adding props for collection
+
+Great! Next, let's take a look at how we can create a dropdown menu of valid options for a user to select what `collection` they want their product carousel to display from the Makeswift editor.
+
+```tsx {1,13,19,22,41-50} filename="core/lib/makeswift/components/my-product-carousel/my-product-carousel.makeswift.tsx" copy
+import { Select, Style } from '@makeswift/runtime/controls';
+
+import { ProductCarousel, ProductsCarouselSkeleton } from '@/vibes/soul/sections/product-carousel';
+import { runtime } from '~/lib/makeswift/runtime';
+
+import { useProducts } from '../../utils/use-products';
+
+interface MSMyProductCarouselProps {
+ className: string;
+ collection: 'featured' | 'best-selling' | 'newest' | 'none';
+}
+
+runtime.registerComponent(
+ function MSMyProductCarousel({ className, collection }: MSMyProductCarouselProps) {
+ const { products, isLoading } = useProducts({
+ collection,
+ additionalProductIds: [],
+ });
+
+ if (isLoading) {
+ return
;
+ }
+
+ if (products == null || products.length === 0) {
+ return
;
+ }
+
+ return
;
+ },
+ {
+ type: 'my-product-carousel',
+ label: 'Catalog / My Product Carousel',
+ props: {
+ className: Style(),
+ collection: Select({
+ label: 'Product collection',
+ options: [
+ { value: 'none', label: 'None (static only)' },
+ { value: 'best-selling', label: 'Best selling' },
+ { value: 'newest', label: 'Newest' },
+ { value: 'featured', label: 'Featured' },
+ ],
+ defaultValue: 'featured',
+ }),
+ },
+ },
+);
+```
+
+First, we need to add a new property to our interface definition called `collection` whose type is defined as a union of valid strings.
+
+Then , we can import `Select()` control, and ensure that each of the `options[value]`'s map to each valid string defined in our `collection` union type.
+
+Now, we can remove the default value originally passed to `collection` in the `useProducts` hook, and are now populating the value with the `collection` prop taken by the component.
+
+## Adding additional product ID's
+
+Now let's do the same for `additionalProductIds`. This one is a bit more involved.
+
+```tsx {1-8,16,22-24,31,33-35,38,66-89} filename="core/lib/makeswift/components/my-product-carousel/my-product-carousel.makeswift.tsx" copy
+import { Combobox, Group, List, Select, Style, TextInput } from '@makeswift/runtime/controls';
+
+import { ProductCarousel, ProductsCarouselSkeleton } from '@/vibes/soul/sections/product-carousel';
+import { runtime } from '~/lib/makeswift/runtime';
+
+import { searchProducts } from '../../utils/search-products';
+import { useProducts } from '../../utils/use-products';
+
+interface MSMyProductCarouselProps {
+ className: string;
+ collection: 'featured' | 'best-selling' | 'newest' | 'none';
+ additionalProducts: Array<{
+ entityId?: string;
+ }>;
+}
+
+runtime.registerComponent(
+ function MSMyProductCarousel({
+ className,
+ collection,
+ additionalProducts,
+ }: MSMyProductCarouselProps) {
+ const additionalProductIds = additionalProducts.map(({ entityId }) => entityId ?? '');
+ const { products, isLoading } = useProducts({
+ collection,
+ additionalProductIds,
+ });
+
+ if (isLoading) {
+ return
;
+ }
+
+ if (products == null || products.length === 0) {
+ return
;
+ }
+
+ return
;
+ },
+ {
+ type: 'my-product-carousel',
+ label: 'Catalog / My Product Carousel',
+ props: {
+ className: Style(),
+ collection: Select({
+ label: 'Product collection',
+ options: [
+ { value: 'none', label: 'None (static only)' },
+ { value: 'best-selling', label: 'Best selling' },
+ { value: 'newest', label: 'Newest' },
+ { value: 'featured', label: 'Featured' },
+ ],
+ defaultValue: 'featured',
+ }),
+ additionalProducts: List({
+ label: 'Additional products',
+ type: Group({
+ label: 'Product',
+ props: {
+ title: TextInput({ label: 'Title', defaultValue: 'Product title' }),
+ entityId: Combobox({
+ label: 'Product',
+ async getOptions(query) {
+ const products = await searchProducts(query);
+
+ return products.map((product) => ({
+ id: product.entityId.toString(),
+ label: product.name,
+ value: product.entityId.toString(),
+ }));
+ },
+ }),
+ },
+ }),
+ getItemLabel(product) {
+ return product?.entityId.label || 'Product';
+ },
+ }),
+ },
+ },
+);
+```
+
+In this case, we can **compose** a few different controls to provide a good editor UX (`List()`, `Group()`, `TextInput()`, `Combobox()`). Each of these controls serve different purposes. You can learn more about each control in the [Makeswift documentation](https://docs.makeswift.com/developer/reference/controls/list).
+
+You'll notice we're using yet another utility function to fetch product data from a proxy API endpoint. It's very important to understand that the React hook fetches data so that the Component can render products to the screen, while the utility function used in `getOptions` is used to render options to choose from when typing in the combobox to select an additional product to add to your carousel.
+
+`getItemLabel` is a function that simply helps make the `List()` UI items under "Additional products” more readable. `getItemLabel` is a function that receives an item for each item returned by `getOptions`.
+
+## Add limit control
+
+Great! Now, I'm thinking that our marketing team should also be able to control how many products the carousel renders. Following the pattern we've been following, let's update the interface definition, bring in some controls from the Makeswift runtime, and then use those controls to tell Makeswift how to provide a value for our new `limit` prop; finally, we'll need to use the `limit` prop inside of our component to limit the number of products returned by the API.
+
+```tsx {5,23,33,41,70} filename="core/lib/makeswift/components/my-product-carousel/my-product-carousel.makeswift.tsx" copy
+import {
+ Combobox,
+ Group,
+ List,
+ Number,
+ Select,
+ Style,
+ TextInput,
+} from '@makeswift/runtime/controls';
+
+import { ProductCarousel, ProductsCarouselSkeleton } from '@/vibes/soul/sections/product-carousel';
+import { runtime } from '~/lib/makeswift/runtime';
+
+import { searchProducts } from '../../utils/search-products';
+import { useProducts } from '../../utils/use-products';
+
+interface MSMyProductCarouselProps {
+ className: string;
+ collection: 'featured' | 'best-selling' | 'newest' | 'none';
+ limit: number;
+ additionalProducts: Array<{
+ entityId?: string;
+ }>;
+}
+
+runtime.registerComponent(
+ function MSMyProductCarousel({
+ className,
+ collection,
+ limit,
+ additionalProducts,
+ }: MSMyProductCarouselProps) {
+ const additionalProductIds = additionalProducts.map(({ entityId }) => entityId ?? '');
+ const { products, isLoading } = useProducts({
+ collection,
+ collectionLimit: limit,
+ additionalProductIds,
+ });
+
+ if (isLoading) {
+ return
;
+ }
+
+ if (products == null || products.length === 0) {
+ return
;
+ }
+
+ return
;
+ },
+ {
+ type: 'my-product-carousel',
+ label: 'Catalog / My Product Carousel',
+ props: {
+ className: Style(),
+ collection: Select({
+ label: 'Product collection',
+ options: [
+ { value: 'none', label: 'None (static only)' },
+ { value: 'best-selling', label: 'Best selling' },
+ { value: 'newest', label: 'Newest' },
+ { value: 'featured', label: 'Featured' },
+ ],
+ defaultValue: 'featured',
+ }),
+ limit: Number({ label: 'Max collection items', defaultValue: 12 }),
+ additionalProducts: List({
+ label: 'Additional products',
+ type: Group({
+ label: 'Product',
+ props: {
+ title: TextInput({ label: 'Title', defaultValue: 'Product title' }),
+ entityId: Combobox({
+ label: 'Product',
+ async getOptions(query) {
+ const products = await searchProducts(query);
+
+ return products.map((product) => ({
+ id: product.entityId.toString(),
+ label: product.name,
+ value: product.entityId.toString(),
+ }));
+ },
+ }),
+ },
+ }),
+ getItemLabel(product) {
+ return product?.entityId.label || 'Product';
+ },
+ }),
+ },
+ },
+);
+```
+
+## Updating types
+
+Now, if we take a moment to pause and look at the prop interface for `ProductCarousel` from VIBES, you'll notice that there are a bunch of props defined (such as `aspectRatio`, `colorScheme`, etc.) that are missing from our `MyProductCarousel` prop interface we defined as `MSMyProductCarouselProps`. Let's go ahead and update that.
+
+Remember, any time you're making a component for editing in Makeswift, you want to think: “Should any of the props my component takes be editable by someone using this component inside of Makeswift?” In our case, it would be great to give Makeswift editors the ability to control whether a carousel `showsButtons` or hides its overflow. In fact, I want Makeswift users to be able to edit almost any of the props that my VIBES `ProductCarousel` component takes.
+
+While we can manually add each of these props to our `MSMyProductCarouselProps` interface definition, we can use the [`ComponentPropsWithoutRef` utility type from React](https://react-typescript-cheatsheet.netlify.app/docs/react-types/componentprops) to make our job easier. This utility extracts the prop types from a component while excluding any internal ref-related props, giving us a clean type that matches the component's public interface.
+
+Let's change our `MSMyProductCarouselProps` definition to use this instead.
+
+```tsx {1,5-7} filename="core/lib/makeswift/components/my-product-carousel/my-product-carousel.makeswift.tsx"
+import { ComponentPropsWithoutRef } from 'react';
+
+// ...
+
+type MSMyProductCarouselProps = ComponentPropsWithoutRef
& {
+ className: string;
+ collection: 'featured' | 'best-selling' | 'newest' | 'none';
+ limit: number;
+ additionalProducts: Array<{
+ entityId?: string;
+ }>;
+};
+
+// ...
+```
+
+This will tell our `MSMyProductCarouselProps` type definition that it should treat any prop accepted by `ProductCarousel` as a valid prop for `MSMyProductCarouselProps`, as well as `collection` and `additionalProducts`.
+
+You might notice the errors that Typescript is flagging. Namely, `Property 'products' is missing in type …`. This makes sense! The `ProductCarousel` component from VIBES requires a `products` prop, but our `MSMyProductCarousel` component does not (since we fetch `products` inside of the `MSMyProductCarousel` wrapper component to pass to `ProductCarousel`). Let's use the `Omit` utility type from Typescript to remove `products` from our type definition:
+
+```tsx {3-6} filename="core/lib/makeswift/components/my-product-carousel/my-product-carousel.makeswift.tsx"
+// ...
+
+type MSMyProductCarouselProps = Omit<
+ ComponentPropsWithoutRef,
+ 'products'
+> & {
+ className: string;
+ collection: 'none' | 'best-selling' | 'newest' | 'featured';
+ limit: number;
+ additionalProducts: Array<{
+ entityId?: string;
+ }>;
+};
+
+// ...
+```
+
+Great! Typescript is happy, and now our `MSMyProductCarousel` component accepts any valid prop that `ProductCarousel` from VIBES also accepts. We can test this by removing the `className` prop from `MSMyProductCarouselProps` - notice that Typescript does not complain. This is because `className` is already a valid prop accepted by `ProductCarousel`, and since our wrapper component prop type definition extends the props from `ProductCarousel`, we do not need to redundantly define it explicitly.
+
+## Additional controls
+
+At this point, we can add a number of extra controls to fill in the rest of our props:
+
+```tsx {2,39,59,102-130} filename="core/lib/makeswift/components/my-product-carousel/my-product-carousel.makeswift.tsx" copy
+import {
+ Checkbox,
+ Combobox,
+ Group,
+ List,
+ Number,
+ Select,
+ Style,
+ TextInput,
+} from '@makeswift/runtime/controls';
+import { ComponentPropsWithoutRef } from 'react';
+
+import { ProductCarousel, ProductsCarouselSkeleton } from '@/vibes/soul/sections/product-carousel';
+import { runtime } from '~/lib/makeswift/runtime';
+
+import { searchProducts } from '../../utils/search-products';
+import { useProducts } from '../../utils/use-products';
+
+type MSMyProductCarouselProps = Omit<
+ ComponentPropsWithoutRef,
+ 'products'
+> & {
+ collection: 'none' | 'best-selling' | 'newest' | 'featured';
+ limit: number;
+ additionalProducts: Array<{
+ entityId?: string;
+ }>;
+};
+
+runtime.registerComponent(
+ function MSMyProductCarousel({
+ className,
+ collection,
+ limit,
+ additionalProducts,
+ ...props
+ }: MSMyProductCarouselProps) {
+ const additionalProductIds = additionalProducts.map(({ entityId }) => entityId ?? '');
+ const { products, isLoading } = useProducts({
+ collection,
+ collectionLimit: limit,
+ additionalProductIds,
+ });
+
+ if (isLoading) {
+ return ;
+ }
+
+ if (products == null || products.length === 0) {
+ return ;
+ }
+
+ return ;
+ },
+ {
+ type: 'my-product-carousel',
+ label: 'Catalog / My Product Carousel',
+ props: {
+ className: Style(),
+ collection: Select({
+ label: 'Product collection',
+ options: [
+ { value: 'none', label: 'None (static only)' },
+ { value: 'best-selling', label: 'Best selling' },
+ { value: 'newest', label: 'Newest' },
+ { value: 'featured', label: 'Featured' },
+ ],
+ defaultValue: 'featured',
+ }),
+ limit: Number({ label: 'Max collection items', defaultValue: 12 }),
+ additionalProducts: List({
+ label: 'Additional products',
+ type: Group({
+ label: 'Product',
+ props: {
+ title: TextInput({ label: 'Title', defaultValue: 'Product title' }),
+ entityId: Combobox({
+ label: 'Product',
+ async getOptions(query) {
+ const products = await searchProducts(query);
+
+ return products.map((product) => ({
+ id: product.entityId.toString(),
+ label: product.name,
+ value: product.entityId.toString(),
+ }));
+ },
+ }),
+ },
+ }),
+ getItemLabel(product) {
+ return product?.entityId.label || 'Product';
+ },
+ }),
+ aspectRatio: Select({
+ label: 'Aspect ratio',
+ options: [
+ { value: '1:1', label: 'Square' },
+ { value: '5:6', label: '5:6' },
+ { value: '3:4', label: '3:4' },
+ ],
+ defaultValue: '5:6',
+ }),
+ colorScheme: Select({
+ label: 'Text color scheme',
+ options: [
+ { value: 'light', label: 'Light' },
+ { value: 'dark', label: 'Dark' },
+ ],
+ defaultValue: 'light',
+ }),
+ showScrollbar: Checkbox({
+ label: 'Show scrollbar',
+ defaultValue: true,
+ }),
+ showButtons: Checkbox({
+ label: 'Show buttons',
+ defaultValue: true,
+ }),
+ hideOverflow: Checkbox({
+ label: 'Hide overflow',
+ defaultValue: true,
+ }),
+ },
+ },
+);
+```
+
+Now, you can see that all of the props are exposed in Makeswift for the user to control.
+
+
+
+## Wrapping up
+
+There you have it! You just created your first Catalyst component that is visually editable in Makeswift, can source data from your BigCommerce catalog, and is built with a combination of VIBES UI components and Next.js primitives.
diff --git a/docs/storefront/catalyst/getting-started/guides/mailchimp-newsletter.mdx b/docs/storefront/catalyst/getting-started/guides/mailchimp-newsletter.mdx
new file mode 100644
index 000000000..7c7ab55af
--- /dev/null
+++ b/docs/storefront/catalyst/getting-started/guides/mailchimp-newsletter.mdx
@@ -0,0 +1,73 @@
+# Adding Mailchimp to Catalyst
+
+## Introduction
+
+In this guide, we're assuming you have installed the [Mailchimp app](https://www.bigcommerce.com/apps/mailchimp/) on your BigCommerce store and already have the script URL Mailchimp provides, as detailed in the [Install scripts section of the Mailchimp docs](https://mailchimp.com/help/connect-or-disconnect-mailchimp-for-bigcommerce/#heading+install+scripts).
+
+
+ Make sure you've read the [Local development
+ documentation](/docs/storefront/catalyst/getting-started/local-development) to get your development environment setup
+ before continuing.
+
+
+If you want more details on the `` component we use in this guide, check out our [Manually installing scripts](/docs/storefront/catalyst/getting-started/guides/manually-installing-scripts) guide.
+
+## Installing the script
+
+Now that you have your Catalyst environment running and your Mailchimp script URL in hand, you are ready to add it to your site.
+
+The script needs to load on every page, so we're going to add it to the default layout, which is shared across all pages:
+
+```tsx {1,25-28} filename="core/app/[locale]/(default)/layout.tsx" copy
+import Script from 'next/script';
+import { setRequestLocale } from 'next-intl/server';
+import { PropsWithChildren } from 'react';
+
+import { Footer } from '~/components/footer';
+import { Header } from '~/components/header';
+
+interface Props extends PropsWithChildren {
+ params: Promise<{ locale: string }>;
+}
+
+export default async function DefaultLayout({ params, children }: Props) {
+ const { locale } = await params;
+
+ setRequestLocale(locale);
+
+ return (
+ <>
+
+
+ {children}
+
+
+
+
+ >
+ );
+}
+
+export const experimental_ppr = true;
+```
+
+Notice we used the `lazyOnload` strategy! This will prevent the script from loading until the browser has processed all the critical interactive elements and scripts.
+
+If you have active Mailchimp pop-ups, when you load your site after making these changes, you'll see the script is loading correctly. Here is a demo of an email signup pop-up offering a discount that was displayed using this method:
+
+
+
+If you don't see a popup, you might not have one configured. You can still see if the Mailchimp script is loading correctly by checking your local storage. In your browser you should see `mcform` keys in **Application -> Local storage**:
+
+
+
+Because you added the script within the default shared layout file, navigating between pages on the site doesn't re-render the widget. The script is only loaded once per session, instead of once every page:
+
+
+
+## Wrapping up
+
+That's it! The Mailchimp script is now loaded in an optimized way.
diff --git a/docs/storefront/catalyst/getting-started/guides/manually-installing-scripts.mdx b/docs/storefront/catalyst/getting-started/guides/manually-installing-scripts.mdx
new file mode 100644
index 000000000..838ec2758
--- /dev/null
+++ b/docs/storefront/catalyst/getting-started/guides/manually-installing-scripts.mdx
@@ -0,0 +1,97 @@
+# Manually Installing Scripts
+
+## Introduction
+
+In this guide, you'll learn how scripts can be manually added to Catalyst. This is a common need for apps in the BigCommerce marketplace. Luckily it's very straightforward to do!
+
+
+ Marketplace apps may automatically inject scripts into your store using the
+ Scripts API. We plan on Catalyst supporting this in the future, once the
+ GraphQL API supports returning all those scripts in a single request. Until
+ this occurs, use this guide to help you add scripts manually to Catalyst.
+
+
+If you want to see a real-world example of an app's script being added to Catalyst, check out the [Adding Mailchimp to Catalyst](/docs/storefront/catalyst/getting-started/guides/mailchimp-newsletter) guide.
+
+## Overview of the Next.js Script component
+
+The preferred method of adding scripts to Catalyst is the `` component from `next/script`, which is designed to optimize the loading of third-party scripts. It enables us to control when and how scripts are loaded. Using it properly can improve the performance and user experience of your site by loading less Javascript up front and enabling the critical site elements to become interactive in less time.
+
+In its most basic form, it looks very similar to a normal script tag:
+
+```tsx {1,6} copy
+import Script from 'next/script';
+
+export default function YourComponent() {
+ return (
+ <>
+
+ >
+ );
+}
+```
+
+Without any props, the script will load using an `afterInteractive` strategy. That means it'll load the script after the page initially starts to render interactive elements in the browser.
+
+There are multiple strategies compatible with Catalyst:
+
+- [`beforeInteractive`](https://nextjs.org/docs/app/api-reference/components/script#beforeinteractive): Load before any Next.js code and before any page hydration occurs. (_e.g. bot detectors and cookie consent managers_)
+- [`afterInteractive`](https://nextjs.org/docs/app/api-reference/components/script#afterinteractive): (default) Load early but after some hydration on the page occurs. (_e.g. tag managers and analytics_)
+- [`lazyOnload`](https://nextjs.org/docs/app/api-reference/components/script#lazyonload): Load during browser idle time. (_e.g. chat support plugins, social media widgets, and marketing pop-ups_)
+
+What is not listed here is the `worker` strategy, which loads the script on a different thread, within a web worker. This is because that strategy is experimental and not yet compatible with two modern technologies Catalyst depends on: Next.js App Router and React Server Components.
+
+For comprehensive details on the Script component, [check out the Next.js documentation](https://nextjs.org/docs/app/api-reference/components/script).
+
+## Installing scripts all pages
+
+To load a script on every page, add it to the default layout, which is shared across all pages:
+
+```tsx {1,25} filename="core/app/[locale]/(default)/layout.tsx" copy
+import Script from 'next/script';
+import { setRequestLocale } from 'next-intl/server';
+import { PropsWithChildren } from 'react';
+
+import { Footer } from '~/components/footer';
+import { Header } from '~/components/header';
+
+interface Props extends PropsWithChildren {
+ params: Promise<{ locale: string }>;
+}
+
+export default async function DefaultLayout({ params, children }: Props) {
+ const { locale } = await params;
+
+ setRequestLocale(locale);
+
+ return (
+ <>
+
+
+ {children}
+
+
+
+
+ >
+ );
+}
+
+export const experimental_ppr = true;
+```
+
+The script will now be loaded once per session.
+
+## Installing scripts on a specific page
+
+The instructions above are focused on the most common use case, which is installing scripts that will be on all pages. That's why we used the shared default layout file.
+
+The same code could be used within a page route file instead of the layout, if you want to target only a single type of page.
+
+For instance, if you wanted a script for product reviews to load only on product pages, you would add it to `core/app/[locale]/(default)/product/[slug]/page.tsx`
+
+## Enabling scripts that run code on every page load
+
+Sometimes scripts have logic that runs each time a page is visited. If you find that the script you install is working on the first page you visit, but not the second, it's likely dependent on additional logic like this.
+
+The `` component has an `onReady` prop that accepts a function that will run every time a page is loaded, even if the script is already loaded. You can view more details about this property [on the Next.js docs](https://nextjs.org/docs/app/api-reference/components/script#onready).
diff --git a/docs/storefront/catalyst/getting-started/local-development.mdx b/docs/storefront/catalyst/getting-started/local-development.mdx
new file mode 100644
index 000000000..a08766b43
--- /dev/null
+++ b/docs/storefront/catalyst/getting-started/local-development.mdx
@@ -0,0 +1,92 @@
+# Local Development
+
+This guide assumes you used the [One-Click Catalyst](/docs/storefront/catalyst/getting-started/workflows/one-click-catalyst) workflow by following the [Getting Started](/docs/storefront/catalyst/getting-started) guide. If not, see the [Manual installation](/docs/storefront/catalyst/development/manual-installation) docs.
+
+After completing the [Getting Started](/docs/storefront/catalyst/getting-started) guide, you should have a sandbox instance of Catalyst. You'll also have two Makeswift sites:
+
+- Production - connected to the deployed Catalyst instance
+- Development - connected to `http://localhost:3000`
+
+The development site is the one you'll use when developing locally. By running the source code locally, developers will have the ability to customize their storefront in any way including:
+
+- [Creating custom components](/docs/storefront/catalyst/getting-started/guides/custom-components)
+- Customize existing components
+- Customizing caching, routing, etc.
+- Integrating with other services like [Algolia](https://www.algolia.com/), [Contentful](https://www.contentful.com/), etc.
+
+Lastly, you'll want to push the source code into a your own Github repository. Let's see how to run the code locally and create your own Github repository.
+
+## Prerequisites
+
+Before you begin, ensure you have met the prerequisites listed below on your local machine.
+
+### Node.js version `20.x`
+
+Catalyst currently supports Node.js version `20.x` ([click here to download Node.js](https://nodejs.org/en/download/package-manager))
+
+### Corepack-enabled `pnpm`
+
+Node.js version `20.x` ships with Corepack by default; you should be able to run `corepack enable` from your terminal to meet this prerequisite (more information on the [official Node.js Corepack documentation](https://nodejs.org/docs/latest-v20.x/api/corepack.html))
+
+### Git version `2.35.x` or later
+
+[Click here to download Git](https://git-scm.com/book/en/v2/Getting-Started-Installing-Git)
+
+### A GitHub account
+
+You can sign up for a free GitHub account on the [official GitHub signup page](https://github.com/join).
+
+## Running locally
+
+In the BigCommerce dashboard for your Catalyst channel, you'll see a few steps under the **Complete Setup** section. The **Start development** section includes a terminal command to run in your local environment.
+
+
+
+This command will do several things:
+
+- Clone the Catalyst repository
+- Set all environment variables
+- Set the `main` branch to match the git ref that was deployed to the BigCommerce partner sandbox account ([more on versioning](/docs/storefront/catalyst/getting-started/versioning))
+- Set the `upstream` remote variable to `https://github.com/bigcommerce/catalyst.git`
+
+
+ NOTE: The `MAKESWIFT_SITE_API_KEY` environment variable is the API key for
+ your development Makeswift site.
+
+
+After running the command in your terminal, start your development server with the following command:
+
+```shell copy
+pnpm dev
+```
+
+By default, Catalyst will run at `http://localhost:3000`. This is also the URL that the development site in Makeswift is connected to. From here, you can open the development Makeswift site in your browser.
+
+
+ If you're locally running project runs at a different port than `3000` you'll
+ need to update the **Host URL** property in Makeswift to match.
+
+
+As you make changes in the code, you'll be able to see them reflected in real time. Note that some changes may require a refresh of the page.
+
+## Creating a Github repository
+
+Now that you have the code running locally, you'll want to move the code to your own source control repository. In this example, we'll use Github, but feel free to use any Git provider you'd like.
+
+Go to [Github](https://github.com) and create a new repository. GitHub will prompt you to choose a name for your repository. For the purposes of this guide, we'll refer to name of the repository as ``. You should replace `` in all of the commands below with your actual repository name.
+
+After you've created your Github repository, you can connect you local repository to the remote repository by running the following command:
+
+```shell copy
+ git remote add origin git@github.com:/.git
+```
+
+Then, you can push your changes to the remote repository by running:
+
+```shell copy
+ git push -u origin main
+```
+
+## Next Steps
+
+You now have your own Github repository that you can continue to iterate on. When you're ready to publish your site, refer to our [Deploying docs](/docs/storefront/catalyst/getting-started/deploying/vercel) for next steps.
diff --git a/docs/storefront/catalyst/getting-started/versioning.mdx b/docs/storefront/catalyst/getting-started/versioning.mdx
new file mode 100644
index 000000000..ff1240148
--- /dev/null
+++ b/docs/storefront/catalyst/getting-started/versioning.mdx
@@ -0,0 +1,125 @@
+# Versioning
+
+Catalyst follows a structured versioning approach to distinguish between stable releases and ongoing development.
+
+To stay updated on our releases, check out the Catalyst [releases page](https://github.com/bigcommerce/catalyst/releases) and the [changelog](https://github.com/bigcommerce/catalyst/blob/canary/core/CHANGELOG.md) on GitHub.
+
+## How we use branches
+
+The `canary` branch contains the latest code in development and is the default branch for the Catalyst repository instead of `main`. Note that the most recent commit in `canary` may not be stable.
+
+For the most recent stable code, refer to [How we use tags](#how-we-use-tags) below.
+
+## How we use tags
+
+We create Github Releases from Git tags to indicate distributable releases of Catalyst. To get the latest stable release of Catalyst, you can use the following tags:
+
+- Catalyst - `@bigcommerce/catalyst-core@latest`
+- Catalyst (with Makeswift) - `@bigcommerce/catalyst-core@makeswift-latest`
+
+
+ Until version 1.0 of Catalyst is released, the release deployed using
+ [One-Click Catalyst](/docs/storefront/catalyst/getting-started/workflows/one-click-catalyst) will be from the
+ `@bigcommerce/catalyst-core@makeswift-canary` tag and **is not guranteed to be
+ stable**. After 1.0 is released, this will move to use the
+ `@bigcommerce/catalyst-core@makeswift-latest` which will be stable.
+
+
+You can also find a list of all [Catalyst release tags](https://github.com/bigcommerce/catalyst/tags) in the Catalyst repository.
+
+## How we handle versioning
+
+Catalyst is moving from a [ZeroVer](https://0ver.org/) versioning strategy to [SemVer](https://semver.org/).
+
+Each release includes a changeset that outlines changes, categorizing them as patch, minor, or major changes based on [SemVer](https://semver.org/) versioning.
+Backwards-incompatible changes will now be tagged with major version bumps.
+While the exact definition of "backwards incompatible" can vary in a customizable product, we'll aim to provide clear communication on updates that might require extra effort to integrate.
+
+These breaking or major changes include:
+
+- Updates that affect the shopper experience.
+- Modifications to the theme interface.
+- Significant internal refactors that, while not altering functionality, create substantial merge conflicts when updating Catalyst.
+
+Minor changes include:
+
+- Adding new functionality that does not interfere with existing features.
+- UI adjustments that do not modify prop interfaces.
+
+Patch changes include:
+
+- Small patches and bug fixes.
+
+## How to handle updates
+
+Catalyst is designed as a starting point for your storefront. We expect merchants to customize it extensively to meet their branding requirements and unique business needs. It's important to understand that the more customizations you add, the more challenging it becomes to merge in new updates and resolve merge conflicts; we understand that this is a very unfortunate trade-off, and as Catalyst continues to evolve, we commit to continuously improving our update guidance to help you keep your project up to date with less effort.
+
+As of Catalyst v1.0.0, we are now including migration guides along with each PR merged into the `canary` and `integrations/makeswift` branches of the Catalyst repository, to make merge conflicts easier to reason about. Migration guides, when relevant, can be found in both the `.changeset/\*.md` file associated with the commit ([example](https://github.com/bigcommerce/catalyst/pull/2370/files#diff-a28d229d79df76c759f288d0cc0c8412e7955d22c5ce46c7b63f3ce6f1982c81)), as well as the PR description that introduced the commit ([example](https://github.com/bigcommerce/catalyst/pull/2370#issue-3118949159)).
+
+Additionally, we have [documented the steps that we follow](https://github.com/bigcommerce/catalyst/blob/canary/CONTRIBUTING.md#keeping-integrationsmakeswift-in-sync-with-canary) whenever we need to pull in updates from `canary` into `integrations/makeswift`. While these steps primarily serve as documentation on how to update the Makeswift integration branch, you may find it helpful to follow those same steps whenever you need to update your own custom Catalyst fork\!
+
+While the exact steps you need to follow vary widely depending on how you've configured your own local Catalyst Git project. Here is one example of the workflow we'd recommend based on the following assumption(s):
+
+- You have a remote named `upstream` pointing to the [`bigcommerce/catalyst` repository on GitHub](https://github.com/bigcommerce/catalyst). If you do not, you can add it with `git remote add upstream ssh://git@github.com/bigcommerce/catalyst.git`, or if you are not using SSH, you can use `git remote add upstream https://github.com/bigcommerce/catalyst.git`.
+
+### Steps to update your local Catalyst project
+
+1. Fetch the latest tags from the `upstream` remote:
+
+ ```bash copy
+ git fetch upstream --tags
+ ```
+
+2. Checkout and pull the latest changes from the primary development branch in your local Catalyst project:
+
+ ```bash copy
+ git checkout your-primary-development-branch
+ git pull origin your-primary-development-branch
+ ```
+
+3. Create a new interim branch to work on your updates and resolve any merge conflicts:
+
+ ```bash copy
+ git checkout -b your-interim-sync-branch
+ ```
+
+4. Merge the latest `@bigcommerce/catalyst-makeswift@latest` tag into your interim branch:
+
+ ```bash
+ git merge @bigcommerce/catalyst-makeswift@latest
+ ```
+
+
+ If you originally created your Catalyst project based on the version of
+ Catalyst without Makeswift, you will need to merge the latest
+ `@bigcommerce/catalyst-core@latest` tag into your interim branch instead of
+ the `@bigcommerce/catalyst-makeswift@latest` tag.
+
+
+5. Resolve any merge conflicts that arise. After resolving any merge conflicts, open a new PR in GitHub to merge your `your-interim-sync-branch` into `your-primary-development-branch`. This PR should be code reviewed and approved by your team before the next steps.
+
+6. Once your PR is approved, the next step is to incorporate the merge commit from `your-interim-sync-branch` into `your-primary-development-branch`. There are a few ways to do this, and the best approach for you will depend on your workflow. Here are a couple of options:
+
+ - **Option 1: Merge your interim branch into your primary development branch using the GitHub UI:** This is the recommended approach if you are not comfortable with using the command line.
+
+ - **Option 2: Rebase your primary development branch on top of your interim branch release:** This is the recommended approach if you are comfortable with using the command line, and want to keep your commit history clean.
+
+ ```bash copy
+ git checkout your-primary-development-branch
+ git rebase your-interim-sync-branch
+ ```
+
+
+ Regardless of which option you choose, your goal is to incorporate the merge
+ commit (the commit with two parent commits) from `your-interim-sync-branch`
+ into `your-primary-development-branch`. This has the effect of establishing a
+ new merge base for your primary development branch, so that the next time you
+ pull in updates from `upstream`, you will not have to resolve any merge
+ conflicts that you've already resolved in your interim branch.
+
+
+7. If you went with option 2 above, you have one more step to complete. Push your changes to the remote `your-primary-development-branch`, which will automatically close the PR you opened in step 5:
+
+ ```bash copy
+ git push origin your-primary-development-branch
+ ```
diff --git a/docs/storefront/catalyst/getting-started/workflows/contribute.mdx b/docs/storefront/catalyst/getting-started/workflows/contribute.mdx
new file mode 100644
index 000000000..ba99f064c
--- /dev/null
+++ b/docs/storefront/catalyst/getting-started/workflows/contribute.mdx
@@ -0,0 +1,3 @@
+# Contribute to Catalyst
+
+We're so happy you're interested in contributing to what we believe is the fastest, most fully-featured storefront in the world. Have a look at our [CONTRIBUTING.md](https://github.com/bigcommerce/catalyst/blob/main/CONTRIBUTING.md) over in the Catalyst GitHub repository to get started!
diff --git a/docs/storefront/catalyst/getting-started/workflows/integrate.mdx b/docs/storefront/catalyst/getting-started/workflows/integrate.mdx
new file mode 100644
index 000000000..778e59594
--- /dev/null
+++ b/docs/storefront/catalyst/getting-started/workflows/integrate.mdx
@@ -0,0 +1,48 @@
+# Build an integration
+
+From the entire Catalyst team, we're excited to build alongside you! Being an integration developer means making it easier and more accessible for merchants to adopt your technology into their composable commerce stack.
+
+Catalyst integrations vary in size and complexity; some Catalyst integrations may communicate with third-party API's and work best when paired with a related app from the [BigCommerce App Marketplace](https://bigcommerce.com/apps), while other Catalyst integrations may be as simple as installing a package from NPM.
+
+No matter the size of your integration, one thing that all Catalyst integrations have in common is that they contain reference code pushed to a branch on the Catalyst upstream repository. This makes it easy to browse every single Catalyst integration in one location:
+
+[https://github.com/bigcommerce/catalyst/branches/all?query=integrations%2F](https://github.com/bigcommerce/catalyst/branches/all?query=integrations%2F)
+
+{/* @todo clean up all the branches on catalyst upstream. people should work from their forks so that branches are easier to skim in upstream */}
+
+This section will guide you through setting up your integration development environment, building a minimal reference implementation of your technology into Catalyst, and finally how to pull in updates from the upstream Catalyst repository to keep your integration up to date.
+
+By the end of this section, you'll be on your way to having your integration listed in the link above.
+
+### Setting up your integration development environment
+
+Since you'll ultimately be contributing code back up to the Catalyst upstream GitHub repository, it makes the most sense for you to create a new branch off of `upstream/main` in your local Catalyst fork:
+
+```shell copy
+git fetch upstream main
+git checkout -b integrations/ upstream/main
+```
+
+Now, you can begin to write the code required to make your integration work!
+
+### Building your integration
+
+There are a handful of lessons we have learned from building integrations into Catalyst ourselves; we wanted to offer those lessons as tips to follow while building your own integration. You should highly consider incorporating these tips into your own workflow, as we have found it has saved us time and headache.
+
+1. Keep your integration code as maintainable as possible. You'll notice that the [`integrations/makeswift` branch](https://github.com/bigcommerce/catalyst/compare/main...integrations/makeswift) has only a single commit, and about 20 changed files. This means that merge conflicts caused rebasing on top of the latest `upstream/main` to update your integration are easier to deal with.
+2. Explicitly call out when your integration makes use of newly introduced environment variables. You'll notice that the [`integrations/makeswift` branch](https://github.com/bigcommerce/catalyst/compare/main...integrations/makeswift) adds a new environment variable called `MAKESWIFT_SITE_API_KEY`; by explicitly listing newly introduced environment variables in a version-controlled file like `.env.example`, it makes it obvious to the consumers of your integration when they need to add new environment variables to make your integration work.
+
+### Open a PR to add your integration to upstream
+
+Now that your branch is ready for public use, please start by opening a new issue in the Catalyst upstream GitHub repository. In your issue, request the creation of an `integrations/` branch. Once the branch is created, you can target it with your pull request to contribute your integration code back upstream.
+
+### Keeping your integration up to date
+
+Keeping your integration up to date is as simple as rebasing your integration branch on top of the latest changes introduced to `upstream/main`, and then opening a PR from your origin remote fork integration branch into the integration branch with the same name on the upstream remote repository.
+
+```shell copy
+git checkout integrations/
+git fetch upstream main
+git pull --rebase upstream main
+git push --force-with-lease origin integrations/
+```
diff --git a/docs/storefront/catalyst/getting-started/workflows/one-click-catalyst.mdx b/docs/storefront/catalyst/getting-started/workflows/one-click-catalyst.mdx
new file mode 100644
index 000000000..30291a2f6
--- /dev/null
+++ b/docs/storefront/catalyst/getting-started/workflows/one-click-catalyst.mdx
@@ -0,0 +1,33 @@
+# One-Click Catalyst
+
+One-Click Catalyst simplifies the process of setting up a fully customizable headless ecommerce storefront with BigCommerce. It not only creates a storefront but also deploys a hosted preview and provisions a corresponding site in BigCommerce’s visual editor, [Makeswift](https://makeswift.com/).
+
+
+
+This code-free starting point allows marketers and content authors to explore Catalyst's features and Makeswift's design capabilities without needing a developer. Specifically, you’ll be able to:
+
+- Customize site theme and design
+- Create and edit layouts
+- Create and edit pages
+- Incorporate BigCommerce product data
+
+
+ Note that the automatically deployed storefront is only intended to be used
+ for previewing your storefront, so you’ll need to follow our
+ [deploying](/docs/storefront/catalyst/getting-started/deploying/vercel) documentation to take your storefront to production.
+
+
+After completing the initial One-click Catalyst setup in the BigCommerce dashboard you will have:
+
+- An instance of Catalyst deployed to the BigCommerce partner sandbox account
+- Two Makeswift sites
+ - Production site - connected to the deployed Catalyst instance
+ - Development site - connected to `http://localhost:3000`
+
+For more details on setting up a storefront using One-Click Catalyst you can refer to our [Getting Started](/docs/storefront/catalyst/getting-started) documentation. Additionally, here are a few more documents that might be helpful.
+
+
+
+
+
+
diff --git a/docs/storefront/catalyst/overview.mdx b/docs/storefront/catalyst/overview.mdx
new file mode 100644
index 000000000..0338cf25c
--- /dev/null
+++ b/docs/storefront/catalyst/overview.mdx
@@ -0,0 +1,73 @@
+# Catalyst
+
+**Catalyst** is the composable, fully customizable headless ecommerce storefront framework for [BigCommerce](https://www.bigcommerce.com/). We've built Catalyst with [Next.js](https://nextjs.org/), [React](https://react.dev/) storefront components, and our [GraphQL Storefront API](/docs/storefront/graphql).
+
+When you use Catalyst, you can create a fully functional storefront using our CLI and get to work without wiring up APIs or building ecommerce components from scratch that are optimized for SEO, accessibility, and performance. Catalyst is designed to take care of the essentials so you can focus your efforts on building your brand and adding those special features that take storefronts to the next level.
+
+You can find a demo version of Catalyst at https://catalyst-demo.site/, hosted on Vercel in a US region.
+
+## Ecommerce functionality
+
+Catalyst has a production-ready business-to-consumer (B2C) ecommerce funnel, including the following features:
+
+* Home page
+* Faceted search on product listing pages (PLPs) which are dynamically created for each category and brand
+* Full-text search
+* Product detail pages (PDPs) which are dynamically created for each product and published to the Catalyst storefront channel
+* Shopping cart
+* Secure redirected headless checkout page, which can be themed to match the styling of the core Catalyst storefront
+* Accounts logged-in customers. This currently includes address management and customer account settings.
+
+
+## Default state of Catalyst
+
+Out-of-the-box, Catalyst has the following capabilities you can use to further build your storefront:
+
+### Brand or retailer experience
+
+* **Platform feature support**. Users' changes made from the BigCommerce store control panel or through APIs appear on the storefront as expected.
+* **Complex catalog support**. Catalyst supports product catalogs of any size, complexity, or update frequency.
+
+### Developer experience
+
+* **Scalable GraphQL implementation**. Catalyst features scalable implementation of the GraphQL Storefront API.
+* **Minimal infrastructure dependencies**. Catalyst can be deployed to any hosting provider that supports Node.js.
+* **Making the most of modern Next.js features**. Catalyst uses [React server components](https://nextjs.org/docs/app/building-your-application/rendering/server-components) and [Suspense](https://react.dev/reference/react/Suspense) boundaries in preparation for [partial pre-rendering](https://nextjs.org/learn/dashboard-app/partial-prerendering).
+* **Components**. Catalyst provides _functionally_ complete ecommerce components and a pre-optimized site architecture for performance, SEO, and accessibility.
+* **Unopinionated visual design**. Visually, Catalyst is a "blank sheet of paper" that's ready to be transformed into a stellar ecommerce brand. Catalyst components use [Tailwind CSS](https://tailwindcss.com/) to accelerate development.
+
+### Shopper experience
+
+* **Optimized shopping experiences**. Catalyst provides a guest shopper experience optimized for speed and supports the dynamic experiences that matter to logged-in customers.
+* **Secure checkout**. Catalyst uses an optimized one-page redirected checkout on our hosted SaaS environment to simplify PCI compliance. By default, your headless storefront never collects or transmits personally identifiable information (PII) such as credit card numbers.
+
+
+ Customers have accounts on the store and can sign in to see orders, manage their accounts, and more.
+ Shoppers browse the storefront but don't have accounts.
+
+
+### Configuration recommendations
+
+We expect you will prioritize some of these features over others as you make Catalyst your own. We recommend exploring the following configurations as you build:
+
+* **A caching backend**. For example, using Vercel KV may improve the performance of our [with-routes middleware](/docs/storefront/catalyst/development/middleware.md).
+* **Using microservices**. Externally hosted databases and third-party APIs can expand the feature set and data persistence available to Catalyst storefronts.
+* **Evaluating middleware**. Assess the costs and benefits of the [default Catalyst middleware](/docs/storefront/catalyst/development/middleware.md). You may tailor the middleware for your use case or remove it altogether.
+* **Additional catalog optimization**. You can tune Catalyst's performance based on your store's catalog size, complexity, and update frequency. For example, a catalog with a small number of products and infrequent updates may be able to take advantage of static generation, whereas a large catalog with frequent changes may lean into an aggressive caching strategy that relies on webhooks.
+
+## Future releases
+
+Over time, we plan to expand Catalyst to include all our default storefront features, including feature parity with our fully hosted [Stencil storefront platform](https://developer.bigcommerce.com/docs/storefront/stencil).
+
+We plan to add full support for B2B Edition in a future release.
+
+We welcome feedback in [GitHub Discussions](https://github.com/bigcommerce/catalyst/discussions) on how we should prioritize these enhancements, where we will provide a public view of our roadmap.
+
+## Resources
+
+* [🤗 BigCommerce Developer Community](/community)
+* [💬 GitHub Discussions](https://github.com/bigcommerce/catalyst/discussions)
+* [GraphQL Storefront API Playground](/graphql-storefront/playground)
+* [GraphQL Storefront API Explorer](/graphql-storefront/explorer)
+* [BigCommerce DevDocs](/docs/build)
+* [MIT license](https://github.com/bigcommerce/catalyst/blob/main/LICENSE)
\ No newline at end of file
diff --git a/docs/storefront/catalyst/reference/client.mdx b/docs/storefront/catalyst/reference/client.mdx
new file mode 100644
index 000000000..508312f7a
--- /dev/null
+++ b/docs/storefront/catalyst/reference/client.mdx
@@ -0,0 +1,179 @@
+# Catalyst Client
+
+Catalyst allows you to retrieve info from BigCommerce's APIs through the Catalyst Client.
+
+```ts
+import { client } from '~/client';
+```
+
+The client simplifies what you need to do to handle requests and responses. For example, you don't need to construct request URLs, configure request headers, or parse JSON responses. The client uses the channel ID and authorization tokens configured in the `.env` file by default.
+
+## Methods
+
+- `fetch()`: allows you to interact with BigCommerce's [GraphQL Storefront API](https://developer.bigcommerce.com/docs/storefront/graphql). You can execute all queries and mutations available in [BigCommerce's Storefront graph](https://developer.bigcommerce.com/graphql-storefront/reference).
+
+- `fetchSitemapIndex(channelId?: string)`: fetches the sitemap index for the store, which provides a URL to the XML sitemap.
+
+## Fetch
+
+The following sections describe the `fetch()` method.
+
+### Parameters
+
+| Parameter name | Type | Required? | Description |
+| - | - | - | - |
+| `document` | object `DocumentDecoration` | Yes | The GraphQL query or mutation you want to execute. It must be in the form of a string or a GraphQL AST (Abstract Syntax Tree) that defines the query.
The `DocumentDecoration` interface supports types from `@graphql-typed-document-node/core` and `TypedQueryDocumentNode`. These ensure the types of variables and results match. The document could be a GraphQL query or mutation. |
+| `variables` | object `TVariables` | No | Variables to be passed to the GraphQL query or mutation. |
+| `customerAccessToken` | string | No | The customer's access token for authenticated requests.
This token is required for operations that need customer authentication (e.g., accessing cart, checkout, orders, or customer data) and can be populated with the `getSessionCustomerAccessToken()` utility function. The token is added as an `X-Bc-Customer-Access-Token` header. |
+| `fetchOptions` | object `FetcherRequestInit` | No | Custom options for the `fetch` request.
`FetcherRequestInit` extends the global `RequestInit` interface in JavaScript, which includes parameters such as `method`, `headers`, `body`, and `options` for caching and credentials. |
+| `channelId` | string | No | Allows you to specify a different storefront channel for the request.
Defaults to the channel ID in the `.env` file. |
+| `errorPolicy` | `none` \| `auth` \| `ignore` \| `all` | No | - `none` (default): The client will throw any errors that are returned from the API. This is useful for ensuring you properly catch errors provided through the API.
- `auth`: The client will throw an error if there are any authorization issues, for example, a missing or invalid Customer Access Token.
- `ignore`: The client will ignore any errors and will just return the data.
- `all` (advance): The client will return the full response with the data, including any errors returned from the API. This advance option lets you customize what happens with the response. |
+| `validateCustomerAccessToken` | boolean | No | - `true` (default): The client will pass a header to the API to return any auth related errors, for example a missing or invalid Customer Access Token.
- `false`: This option should be set when making a request that has customer-specific information, but it shouldn't throw an error. For example, a query to fetch the guest vs. shopper categories in the header. |
+
+### Request headers
+
+The `fetch` method automatically sets the following headers:
+
+- `"Content-Type": "application/json"`
+- `"User-Agent": `
+- `"X-Bc-Customer-Access-Token": ` (when `customerAccessToken` is provided)
+- `"X-Bc-Error-On-Invalid-Customer-Access-Token": ` (when `validateCustomerAccessToken` is `true`)
+
+### Return value
+
+The `fetch` method returns a promise that resolves to a response object containing the requested data. The response follows the structure defined in the GraphQL query.
+
+- Return Type: `Promise>`
+
+ The `BigCommerceResponse` type wraps the actual data returned from the API, where `TResult` represents the expected shape of the response data.
+
+ Inside `BigCommerceResponse`, the `data` field holds the actual data returned by the GraphQL API, which matches the structure of the query or mutation you executed.
+
+- `Error Handling`:
+
+ If the response is not `ok` (i.e., the request fails), the method throws a `BigCommerceAPIError`, which includes the HTTP status and any GraphQL errors returned in the response.
+
+### Examples
+
+#### Get customer orders (authenticated request)
+
+```ts
+import { client } from '~/client';
+import { graphql } from '~/client/graphql';
+import { getSessionCustomerAccessToken } from '~/auth';
+
+const GET_CUSTOMER_ORDERS = graphql(`
+ query getCustomerOrders {
+ customer {
+ orders {
+ edges {
+ node {
+ entityId
+ orderNumber
+ status
+ }
+ }
+ }
+ }
+ }
+`);
+
+const customerAccessToken = await getSessionCustomerAccessToken();
+
+const response = await client.fetch({
+ document: GET_CUSTOMER_ORDERS,
+ customerAccessToken,
+ fetchOptions: customerAccessToken ? { cache: 'no-store' } : { next: { revalidate } },
+});
+
+```
+
+#### Get product reviews (unauthenticated request)
+
+```ts
+import { client } from '~/client';
+import { graphql } from '~/client/graphql'; // A utility function for handling GraphQL queries
+
+const GET_PRODUCT_REVIEWS_QUERY = graphql(`
+ query getProductReviews($productIds: [Int!]) {
+ site {
+ products(entityIds: $productIds) {
+ edges {
+ node {
+ reviews {
+ edges {
+ node {
+ author {
+ name
+ }
+ title
+ text
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+`);
+
+const response = await client.fetch({
+ document: GET_PRODUCT_REVIEWS_QUERY, // GraphQL query
+ variables: { productIds: [101, 202] }, // Query variables
+ fetchOptions: { cache: 'no-store' }, // Optional fetch options
+});
+
+console.log(response.data);
+```
+
+The example output in the console:
+
+```ts
+{
+ "site": {
+ "products": [
+ {
+ "reviews": {
+ "edges": [
+ {
+ "node": {
+ "author": {
+ "name": "John Doe"
+ },
+ "title": "Great product",
+ "text": "This product exceeded my expectations!"
+ }
+ },
+ {
+ "node": {
+ "author": {
+ "name": "Jane Smith"
+ },
+ "title": "Good value",
+ "text": "Worth the price, very satisfied."
+ }
+ }
+ ]
+ }
+ }
+ ]
+ }
+}
+```
+
+#### Error handling
+
+The `BigCommerceAPIError` class provides error handling and is instantiated when the fetch fails.
+
+```ts
+if (!response.ok) {
+ throw await BigCommerceAPIError.createFromResponse(response);
+}
+```
+
+### Logging
+
+You can log the request's operation name, type, and execution time to the console, along with the [query complexity](https://developer.bigcommerce.com/docs/storefront/graphql#complexity-limits).
+
+To use this feature, enable logging through the [`CLIENT_LOGGER` environment variable](/docs/storefront/catalyst/development/environment-variables#client_logger). When you run the `fetch` method, it will invoke the `requestLogger()` function internally to capture and log the information.
diff --git a/docs/storefront/catalyst/reference/security.mdx b/docs/storefront/catalyst/reference/security.mdx
new file mode 100644
index 000000000..cf5293d14
--- /dev/null
+++ b/docs/storefront/catalyst/reference/security.mdx
@@ -0,0 +1,54 @@
+# Security
+
+Since Catalyst is optimized for production ecommerce sites utilizing BigCommerce, it benefits from the security standards of BigCommerce's platform in addition to the implementation of strong best practices for the Next.js framework.
+
+## BigCommerce certifications
+
+All commerce operations and checkout functionality by default utilize BigCommerce's GraphQL APIs and hosted checkout experience, which are backed by a robust set of data privacy and security certifications:
+
+- **CCPA**
+- **CSA STAR**
+- **EU-US DPF**
+- **FIPS 140-2**
+- **GDPR**
+- **ISO 22301**
+- **ISO 27001 SoA**
+- **ISO 27001:2022**
+- **ISO 27017**
+- **ISO 27018**
+- **ISO 27701**
+- **PCI DSS**
+- **Privacy Shield**
+- **RH-ISAC**
+- **SOC 1, SOC 2, SOC 3**
+- **TX-RAMP**
+- **Visa Service Provider**
+
+You can learn more about these certifications and request a security review at [security.bigcommerce.com](https://security.bigcommerce.com).
+
+### Security vulnerability monitoring
+
+Catalyst is a part of BigCommerce's bug bounty program and the security team actively monitors and responds to reports, in addition to automatically scanning and manually accessing this codebase internally. Learn more within SECURITY.md [here](https://github.com/bigcommerce/catalyst/blob/main/SECURITY.md).
+
+## Best practices implemented
+
+### Limited data access
+
+By default, Catalyst is 100% powered by a GraphQL Storefront API that has zero access to admin functionality. It's scoped only to the storefront and either anonymous or authenticated shoppers.
+
+### Content Security Policy (CSP)
+
+Catalyst implements a default Content Security Policy (CSP), which can be extended to meet your organization’s specific security standards. This enables you to specify the sources of content that are allowed to be loaded on your site, which helps to prevent Cross-Site Scripting (XSS) and data injection attacks.
+
+You can customize your CSP policy in the codebase [here](https://github.com/bigcommerce/catalyst/blob/main/core/lib/content-security-policy.ts#L10).
+
+## Framework benefits
+
+### Next.js security record
+
+The Next.js framework used by Catalyst has a solid track record of security, aided by hundreds of active contributors, including a consistently high [package health score](https://snyk.io/advisor/npm-package/next) as reported by Snyk.
+
+
+### Industry adoption
+
+You're in good company! Next.js is trusted by many companies handling millions of users and sensitive data across various sectors, including commerce, ticketing, and media. Explore some of these use cases [here](https://nextjs.org/showcase).
diff --git a/docs/storefront/catalyst/reference/testing.mdx b/docs/storefront/catalyst/reference/testing.mdx
new file mode 100644
index 000000000..2275800ba
--- /dev/null
+++ b/docs/storefront/catalyst/reference/testing.mdx
@@ -0,0 +1,33 @@
+# Testing
+
+We use Playwright to test our components and verify workflows on the UI. To learn more, see the [official website documentation](https://playwright.dev/docs/intro).
+
+To run the UI tests locally:
+
+1. Set up the environment variable required to point the tests to either a hosted or local Catalyst instance.
+
+```shell copy
+PLAYWRIGHT_TEST_BASE_URL='https://catalyst-demo.site' || 'http://localhost:3000'
+# Optional: Some tests will fail without these credentials.
+# The environment variables allow automatic creation of customer accounts. These will cleanup themselves after the tests are done.
+BIGCOMMERCE_ACCESS_TOKEN=""
+BIGCOMMERCE_STORE_HASH=""
+```
+
+2. Navigate to the test directory:
+
+```shell copy
+cd core/
+```
+
+3. Run all UI tests in Chromium:
+
+```shell copy
+pnpm exec playwright test tests/ui/ --project=tests-chromium
+```
+
+4. Run a specific test in Chromium:
+
+```shell copy
+pnpm exec playwright tests/visual-regression/components/badge.spec.ts --project=tests-chromium
+```
diff --git a/docs/storefront/catalyst/reference/wishlists.mdx b/docs/storefront/catalyst/reference/wishlists.mdx
new file mode 100644
index 000000000..09ecabb15
--- /dev/null
+++ b/docs/storefront/catalyst/reference/wishlists.mdx
@@ -0,0 +1,63 @@
+# Wishlists
+
+## Overview
+
+Catalyst includes built-in support for wishlists, providing functionality similar to what’s available in Stencil. The implementation enables customers to save products to one or more wishlists directly from the Product Detail Page (PDP), manage those wishlists under their account, and optionally share them publicly.
+
+This documentation outlines the key behaviors, caveats, and code structure so developers can understand or extend the wishlist functionality in Catalyst storefronts.
+
+
+## Functionality
+
+* Users can add products to a wishlist directly from the PDP using the wishlist button.
+* Users can manage multiple wishlists (view, create, rename, delete) from their account page.
+* Wishlists can be shared publicly via unique URLs.
+
+
+## UX Behavior
+
+| Scenario | Behavior |
+| ----- | ----- |
+| User adds a product with selected options | The selected options are added to the wishlist *if* they form a complete variant (SKU). |
+| User is signed out and clicks wishlist button | They are prompted to sign in. After signing in, they are redirected back to the PDP to finish the action. |
+
+
+## Developer Notes
+
+This feature is **already implemented** in Catalyst. Developers **do not need to build wishlist functionality from scratch**, but may want to:
+
+* Customize the UI/UX of wishlist modals or buttons
+
+* Integrate analytics/events
+
+* Modify backend logic for wishlist creation and updates
+
+
+## Key Code Paths
+
+Here’s where to find and modify wishlist functionality in the Catalyst codebase:
+
+### Account Pages
+
+| Path | Description |
+| ----- | ----- |
+| `app/[locale]/(default)/account/wishlists` | Main Wishlists page inside the customer’s account dashboard. |
+| `app/[locale]/(default)/account/wishlists/[id]` | Wishlist details page (shows products within a specific wishlist). |
+| `app/[locale]/(default)/account/wishlists/_actions` | Server actions (GraphQL mutations for create, update, delete, rename). |
+| `app/[locale]/(default)/account/wishlists/_components` | Components related to the Account wishlist UI. |
+| `app/[locale]/(default)/account/wishlists/modals.tsx` | Defines wishlist modals, such as “Create a new wishlist.” |
+
+### Public Wishlists
+
+| Path | Description |
+| ----- | ----- |
+| `app/[locale]/(default)/wishlist/[token]` | Page route for publicly shared wishlists. |
+
+## Shared & PDP Components
+
+| Path | Description |
+| ----- | ----- |
+| `components/wishlist/` | Common/shared wishlist components used across both Account and Public views. |
+| `components/wishlist/share-button.tsx` | Share button used on both Account and Public wishlist pages. |
+| `app/[locale]/(default)/product/[slug]/_components/wishlist-button` | PDP-specific wishlist button component. |
+