Skip to main content

Navigation Preview

The Navigation Editor has a preview popup. The popup opens the storefront with the navigation_preview_id query parameter. The theme must send the value to the navigation call as a header. The theme must also render a navigation item that is still a draft.

Read Theme Editor Preview first. Navigation preview reuses the same resolver.

Step 1: Forward the header

Send the value on every call to getNavigations. The call can be in pageDataResolver or in GlobalDataResolver.

export async function pageDataResolver(data) {
const {
fpi,
router,
themeId,
query: { isEdit, previewId, navigationPreviewId } = {},
cookies,
headers: { userData, userAgent, userGroups: userGroupsFromHeaders } = {},
} = data;

const userGroupsFromCookie = cookies?.userGroups;
const requestHeaders = {};

if (userGroupsFromHeaders || userGroupsFromCookie) {
requestHeaders["user-groups"] = userGroupsFromHeaders || userGroupsFromCookie;
requestHeaders["user_groups"] = userGroupsFromHeaders || userGroupsFromCookie;
}

// Send the navigation preview identifier as a header
if (navigationPreviewId) {
requestHeaders["navigation_preview_id"] = navigationPreviewId;
}

// Keep the value for client-side navigation requests
fpi.custom.setValue("navigationPreviewId", navigationPreviewId || null);

if (pageValue && pageValue !== currentPageInStore) {
const params = { pageValue, themeId, requestHeaders, company };
if (isEdit) params.filters = "false";
if (previewId) params.previewId = previewId;

APIs.push(fpi.theme.getPage(params));
APIs.push(fpi.content?.getNavigations({ requestHeaders }));
}

return await Promise.all(APIs).catch(console.log);
}

The custom state keeps the identifier after hydration. A client-side navigation request has no query parameter of its own.

Step 2: Accept a draft navigation in the selector

The live storefront shows an approved navigation only. Preview mode must also show a draft. Add an isPreview option to the navigation selector of the theme.

/**
* Selects the navigation for a platform and an orientation.
*
* @param {Object} navigationData - Response that holds the navigation items
* @param {string} targetPlatform - 'web', 'mobile', or 'desktop'
* @param {string} targetOrientation - 'portrait' or 'landscape'
* @param {string} orientationPosition - 'top', 'bottom', 'left', or 'right'
* @param {Object} options - Options
* @param {boolean} options.isPreview - True shows an item with any status
* @returns {Object|null} The selected navigation item, or null
*/
export function selectNavigation(
navigationData,
targetPlatform,
targetOrientation = "portrait",
orientationPosition = "top",
options = {}
) {
const { isPreview = false } = options;

if (!navigationData?.items || !Array.isArray(navigationData.items)) {
console.warn("Invalid navigation data provided");
return null;
}

if (!targetPlatform) {
console.warn("Target platform must be specified");
return null;
}

// Preview mode accepts any status. Live mode accepts an approved item only.
const activeNavigations = navigationData.items.filter((item) => {
if (isPreview && item.active !== false) return true;
if (item.status === "approved" && item.active !== false) return true;
return false;
});

if (activeNavigations.length === 0) {
console.warn("No active navigation items found");
return null;
}

return activeNavigations[0];
}

Step 3: Read the flag in the header hook

const useHeader = (fpi) => {
const CONTENT = useGlobalStore(fpi.getters.CONTENT);
const isMobile = useIsMobile();

// Read the identifier that the resolver stored
const customConfig = fpi.getters.CUSTOM_VALUE(fpi.store.getState());
const isPreview = Boolean(customConfig?.navigationPreviewId);

return {
navigation:
selectNavigation(
CONTENT?.navigation || [],
"web",
isMobile ? "portrait" : "landscape",
"top",
{ isPreview }
)?.navigation?.filter((item) =>
"active" in item ? item.active === true : true
) || [],
};
};

export default useHeader;

Verification

  1. Change the navigation in the Navigation Editor. Do not approve the change.
  2. Open the preview popup. Confirm that the header shows the draft navigation.
  3. Open the live storefront. Confirm that the header shows the approved navigation.
  4. Move to another route inside the preview tab. Confirm that the draft navigation stays.

Was this section helpful?