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
- Change the navigation in the Navigation Editor. Do not approve the change.
- Open the preview popup. Confirm that the header shows the draft navigation.
- Open the live storefront. Confirm that the header shows the approved navigation.
- Move to another route inside the preview tab. Confirm that the draft navigation stays.