How to add or change a guided product tour for a feature.
Every sidebar navigation item has a tour. tests/integration/cross-package/saas/product-tour-anchors.test.ts fails when a navigation item has no tour, when a tour points at an anchor that no source file renders, or when a tour's navPath differs from the navigation href.
packages/foundation/src/product-tours.ts. A plain page uses featureTour({ area, id, minutes, navItem, navPath, permission }), which opens the page from the menu and explains it. Pass create: true when the page's create button carries data-tour="page.create". Add the id to productTourIds and the navigation item to productTourNavItems.permission, so the tour is offered to the same people who see the menu item.apps/saas/features/product-tour/product-tour-feature-copy.ts, and the same entries to en.po, nl.po and de.po.Mark the element with data-tour="<anchor>" as a literal attribute, add the anchor to the ProductTourAnchor union and use it in the step. Never anchor on classes, text or DOM structure. A disabled control does not carry the attribute, so the tour skips that step instead of stalling.
Call t() only inside the copy hooks, where t comes from useExtracted in the same scope. Step copy ids are <tourId>.<stepId>.title and <tourId>.<stepId>.body. The dev server rewrites the catalogs, so stop it before you add entries by hand and check git diff on the .po files before you commit.
OrganizationProductTours in the organization layout passes the tour workspace to the client as a JSON string, which parseProductTourWorkspace validates with Zod. Passing the same data as an object prop made server rendering fail with "Maximum call stack size exceeded" on pages with search params; the cause was not found. Keep the string prop, and do not copy the pattern elsewhere without that reason.
?tour=<id> starts a tour. The proxy also stores the id in a five-minute product-tour cookie, because hub pages such as /content and the sign-in flow redirect and drop the query string. useProductTourAutoStart reads the parameter or the cookie once and deletes the cookie after the page has settled.
Last updated on