Skip to main content
Version: 3.x

Quick orders

Front-Commerce's QuickOrder component allows customers to add products to their cart by entering an SKU and a quantity. This component is self-contained and renders a compact form designed to be integrated into different contexts. This guide explains how to integrate this feature into your application.

The <QuickOrder /> component adds a user interface allowing your customers to directly order items based on their SKU

Example with the component added to the default&#39;s theme

Integrate QuickOrder into your project

You must include the component in your page with the following lines:

import React from "react";
import QuickOrder from "theme/modules/QuickOrder";

const MyComponent = () => {
return (
<div>
<QuickOrder />
</div>
);
};

If you need to customize the component, you can, of course, override it.

Enable the Quick Order page

The /quick-order route is gated by the quickOrder extension feature flag. When the flag is inactive, the route returns 404 and no link is rendered in the theme-chocolatine header. The flag is enabled automatically by @front-commerce/adobe-b2b and @front-commerce/gezy-marketplace.

To enable it from any other extension or project, register the feature in your extension's unstable_lifecycleHooks.onFeaturesInit:

my-extension/src/index.ts
return defineRemixExtension({
// ...
unstable_lifecycleHooks: {
onFeaturesInit: (hooks) => {
hooks.registerFeature("quickOrder", { flags: { enabled: true } });
},
},
});

Once enabled, a lightning icon appears in the theme-chocolatine header (between search and wishlist, desktop only) and points to /quick-order. The flag is resolved at build time — toggling it through an environment variable at runtime is not supported.

Configure the Quick Order page

The page configuration is split across two override points so that you only touch the layer you actually need to customize:

  • theme/modules/CsvEntries/useCsvEntriesConfig — the CSV codec (columns, delimiter, quoting). See the dedicated CSV format guide. Overriding the codec applies automatically to every feature that composes it — the Quick Order upload, the template download, and the order detail CSV download — keeping them in sync by construction. The codec mechanics (parseCsv, generateCsv and the associated types) live in @front-commerce/theme-chocolatine/csv and are deliberately outside the theme override surface.
  • theme/pages/QuickOrder/useQuickOrderConfig — the Quick Order page bindings (sample rows for the downloadable template, row Picker, submit mutation, configuration modal). Override this hook when you need to swap how the page renders or submits rows. QuickOrderConfig extends CsvEntriesConfig, so you still get every codec field on it.

Customize the Quick Order page (page bindings)

Override useQuickOrderConfig to change the row picker, the submit mutation or the configuration modal.

my-extension/src/theme/pages/QuickOrder/useQuickOrderConfig.ts
import type { QuickOrderConfig } from "theme/pages/QuickOrder/defaultConfig";
import myQuickOrderConfig from "./myQuickOrderConfig";

const useQuickOrderConfig = (): QuickOrderConfig => myQuickOrderConfig;

export default useQuickOrderConfig;

The QuickOrderConfig type (from theme/pages/QuickOrder/defaultConfig) extends CsvEntriesConfig with these page-specific fields:

FieldRequiredPurpose
csvSampleLinesyesRows used to build the downloadable CSV template on the Quick Order page.
PickeryesComponent used to render each row. Receives the full entry as a prop so it can read any custom field your CSV columns added.
useSubmitItemsyesHook that returns the function called when the user clicks "Add to cart". Receives the valid entries and must return a QuickOrderSubmitResult (success flag, optional error message and per-product errors).
needsConfigurationyes(entry) => boolean — drives the visibility of a "Configure" button on each row. The default flags configurable products imported via CSV (rows whose resolved product still carries configurations).
useConfigurationModalyesHook called once at the page level. Returns { showFor(entry, onComplete), modalElement }modalElement is rendered by the page, showFor is invoked by the page (on "Configure" click) and by custom pickers (via the optional openConfigurationModal prop, see below).

The default config (defaultQuickOrderConfig) wires the standard SKU-based flow against the addMultipleItemsToCart core mutation. Extensions can import it and spread its fields to override only what changes.

Example: replace the submit mutation

myQuickOrderConfig.ts
import { defaultQuickOrderConfig } from "theme/pages/QuickOrder/defaultConfig";
import useMyCustomSubmit from "./useMyCustomSubmit";

const myQuickOrderConfig = {
...defaultQuickOrderConfig,
useSubmitItems: useMyCustomSubmit,
};

export default myQuickOrderConfig;

The useSubmitItems hook is called once at the page level, so it can wire loaders, mutations or even a useRevalidator to refresh the rest of the page when items are added.

Example: plug a custom configuration modal

useConfigurationModal lets you replace the default per-row modal (which reuses SelectProductConfigurationModal) with your own. The hook is called once at the page level and must return:

  • modalElement: the JSX rendered once near the top of the page (typically a <Modal> whose isOpen is driven by internal state).
  • showFor(entry, onComplete): opens the modal for a given entry. onComplete must be called only when the user validates — closing the modal without validating must not invoke it (so a manual pick that gets cancelled does not create a row).
myUseConfigurationModal.ts
import { useCallback, useRef, useState } from "react";
import type {
QuickOrderConfigurationModal,
QuickOrderEntry,
} from "theme/pages/QuickOrder/defaultConfig";

const useMyConfigurationModal = (): QuickOrderConfigurationModal => {
const [isOpen, setIsOpen] = useState(false);
const [entry, setEntry] = useState<QuickOrderEntry | null>(null);
const onCompleteRef = useRef<((entry: QuickOrderEntry) => void) | null>(null);

const close = useCallback(() => {
setIsOpen(false);
setEntry(null);
onCompleteRef.current = null;
}, []);

const showFor = useCallback<QuickOrderConfigurationModal["showFor"]>(
(nextEntry, onComplete) => {
onCompleteRef.current = onComplete;
setEntry(nextEntry);
setIsOpen(true);
},
[],
);

const handleValidate = () => {
if (!entry || !onCompleteRef.current) return;
onCompleteRef.current({ ...entry /* + your custom bake */ });
close();
};

return {
showFor,
modalElement: (
<MyCustomModal
isOpen={isOpen}
product={entry?.product}
onValidate={handleValidate}
onRequestClose={close}
/>
),
};
};

export default useMyConfigurationModal;

Auto-pop the modal at pick time

A custom Picker (passed via config.Picker) can opt in to a new openConfigurationModal prop. The page forwards the modal's showFor through this prop so the picker can intercept its inner pick callback and only emit onStatusUpdate("valid", …) once the modal has been validated — mirroring how <ProductPicker> handles configurable products internally.

MyPicker.tsx
import QuickOrder from "theme/modules/QuickOrder";
import type { QuickOrderPickerProps } from "theme/pages/QuickOrder/defaultConfig";

const MyPicker = ({
entry,
onStatusUpdate,
openConfigurationModal,
...rest
}: QuickOrderPickerProps) => {
const handleStatusUpdate = (status, product, statusText) => {
if (status === "valid" && product && openConfigurationModal) {
// Intercept: open the modal, only emit "valid" once the user validates.
openConfigurationModal({ ...(entry ?? {}), product }, (updatedEntry) => {
onStatusUpdate?.("valid", updatedEntry.product, statusText);
});
return;
}
onStatusUpdate?.(status, product, statusText);
};

return (
<QuickOrder
{...rest}
sku={entry?.sku}
onStatusUpdate={handleStatusUpdate}
/>
);
};

The default picker ignores openConfigurationModal, so a config that does not need auto-pop simply does not provide a custom picker.