{"slug":"tour","title":"Tour","description":"Used for creating product tours and onboarding.","contentType":"component","framework":"react","content":"A tour guides users through product features with step-by-step overlays.\n\n## Resources\n\n\n[Latest version: v1.35.3](https://www.npmjs.com/package/@zag-js/tour)\n[Logic Visualizer](https://zag-visualizer.vercel.app/tour)\n[Source Code](https://github.com/chakra-ui/zag/tree/main/packages/machines/tour)\n\n\n\n**Features**\n\n- Supports different step types such as \"dialog\", \"floating\", \"tooltip\" or\n  \"wait\".\n- Supports customizable content per step.\n- Wait steps for waiting for a specific selector to appear on the page before\n  showing the next step.\n- Flexible positioning of the tour dialog per step.\n- Progress tracking shows users their progress through the tour.\n\n## Installation\n\nInstall the tour package:\n\n```bash\nnpm install @zag-js/tour @zag-js/react\n# or\nyarn add @zag-js/tour @zag-js/react\n```\n\n## Anatomy\n\nCheck the tour anatomy and part names.\n\n> Each part includes a `data-part` attribute to help identify them in the DOM.\n\n\n\n## Usage\n\nImport the tour package:\n\n```jsx\nimport * as tour from \"@zag-js/tour\"\n```\n\nThese are the key exports:\n\n- `machine` - State machine logic.\n- `connect` - Maps machine state to JSX props and event handlers.\n\nThen use the framework integration helpers:\n\n```tsx\nimport * as tour from \"@zag-js/tour\"\nimport { useMachine, normalizeProps, Portal } from \"@zag-js/react\"\nimport { useId } from \"react\"\n\nfunction Tour() {\n  const service = useMachine(tour.machine, { id: useId(), steps })\n  const api = tour.connect(service, normalizeProps)\n  return (\n    <div>\n      <div>\n        <button onClick={() => api.start()}>Start Tour</button>\n        <div id=\"step-1\">Step 1</div>\n      </div>\n      {api.step && api.open && (\n        <Portal>\n          {api.step.backdrop && <div {...api.getBackdropProps()} />}\n          <div {...api.getSpotlightProps()} />\n          <div {...api.getPositionerProps()}>\n            <div {...api.getContentProps()}>\n              {api.step.arrow && (\n                <div {...api.getArrowProps()}>\n                  <div {...api.getArrowTipProps()} />\n                </div>\n              )}\n\n              <p {...api.getTitleProps()}>{api.step.title}</p>\n              <div {...api.getDescriptionProps()}>{api.step.description}</div>\n              <div {...api.getProgressTextProps()}>{api.getProgressText()}</div>\n\n              {api.step.actions && (\n                <div>\n                  {api.step.actions.map((action) => (\n                    <button\n                      key={action.label}\n                      {...api.getActionTriggerProps({ action })}\n                    >\n                      {action.label}\n                    </button>\n                  ))}\n                </div>\n              )}\n\n              <button {...api.getCloseTriggerProps()}>X</button>\n            </div>\n          </div>\n        </Portal>\n      )}\n    </div>\n  )\n}\n\nconst steps: tour.StepDetails[] = [\n  {\n    type: \"dialog\",\n    id: \"start\",\n    title: \"Ready to go for a ride\",\n    description: \"Let's take the tour component for a ride and have some fun!\",\n    actions: [{ label: \"Let's go!\", action: \"next\" }],\n  },\n  {\n    id: \"logic\",\n    title: \"Step 1\",\n    description: \"This is the first step\",\n    target: () => document.querySelector(\"#step-1\"),\n    placement: \"bottom\",\n    actions: [\n      { label: \"Prev\", action: \"prev\" },\n      { label: \"Next\", action: \"next\" },\n    ],\n  },\n  {\n    type: \"dialog\",\n    id: \"end\",\n    title: \"Amazing! You got to the end\",\n    description: \"Like what you see? Now go ahead and use it in your project.\",\n    actions: [{ label: \"Finish\", action: \"dismiss\" }],\n  },\n]\n```\n\n### Using step types\n\nThe tour machine supports different types of steps, allowing you to create a\ndiverse and interactive tour experience. The available step types are defined in\nthe `StepType` type:\n\n- `\"tooltip\"`: Displays the step content as a tooltip, typically positioned near\n  the target element.\n\n- `\"dialog\"`: Shows the step content in a modal dialog centered on screen,\n  useful for starting or ending the tour. This usually doesn't have a `target`\n  defined.\n\n- `\"floating\"`: Presents the step content as a floating element, which can be\n  positioned flexibly on the screen. This usually doesn't have a `target`\n  defined.\n\n- `\"wait\"`: A special type that waits for a specific condition before proceeding\n  to the next step.\n\n```tsx\nconst steps: tour.StepDetails[] = [\n  // Tooltip step\n  {\n    id: \"step-1\",\n    type: \"tooltip\",\n    placement: \"top-start\",\n    target: () => document.querySelector(\"#target-1\"),\n    title: \"Tooltip Step\",\n    description: \"This is a tooltip step\",\n  },\n\n  // Dialog step\n  {\n    id: \"step-2\",\n    type: \"dialog\",\n    title: \"Dialog Step\",\n    description: \"This is a dialog step\",\n  },\n\n  // Floating step\n  {\n    id: \"step-3\",\n    type: \"floating\",\n    placement: \"top-start\",\n    title: \"Floating Step\",\n    description: \"This is a floating step\",\n  },\n\n  // Wait step\n  {\n    id: \"step-4\",\n    type: \"wait\",\n    title: \"Wait Step\",\n    description: \"This is a wait step\",\n    effect({ next }) {\n      // do something and go next\n      // you can also return a cleanup\n    },\n  },\n]\n```\n\n### Configuring actions\n\nEvery step supports a list of actions that are rendered in the step footer. Use\nthe `actions` property to define each action.\n\n```tsx\nconst steps: tour.StepDetails[] = [\n  {\n    id: \"step-1\",\n    type: \"dialog\",\n    title: \"Dialog Step\",\n    description: \"This is a dialog step\",\n    actions: [{ label: \"Show me a tour!\", action: \"next\" }],\n  },\n]\n```\n\n### Changing tooltip placement\n\nUse the `placement` property to define the placement of the tooltip.\n\n```tsx {5}\nconst steps: tour.StepDetails[] = [\n  {\n    id: \"step-1\",\n    type: \"tooltip\",\n    placement: \"top-start\",\n    // ...\n  },\n]\n```\n\n### Hiding the arrow\n\nSet `arrow: false` in the step property to hide the tooltip arrow. This is only\nuseful for tooltip steps.\n\n```tsx {5}\nconst steps: tour.StepDetails[] = [\n  {\n    id: \"step-1\",\n    type: \"tooltip\",\n    arrow: false,\n  },\n]\n```\n\n### Hiding the backdrop\n\nSet `backdrop: false` in the step property to hide the backdrop. This applies to\nall step types except the `wait` step.\n\n```tsx {5}\nconst steps: tour.StepDetails[] = [\n  {\n    id: \"step-1\",\n    type: \"dialog\",\n    backdrop: false,\n  },\n]\n```\n\n### Step Effects\n\nStep effects are functions that are called before a step is opened. They are\nuseful for adding custom logic to a step.\n\nThis function provides the following methods:\n\n- `next()`: Call this method to move to the next step.\n- `goto(id)`: Jump to a specific step by id.\n- `dismiss()`: Dismiss the tour immediately.\n- `show()`: Call this method to show the current step.\n- `update(details: Partial<StepBaseDetails>)`: Call this method to update the\n  current step details (for example, after data is fetched).\n\n```tsx\nconst steps: tour.StepDetails[] = [\n  {\n    id: \"step-1\",\n    type: \"tooltip\",\n    effect({ next, show, update }) {\n      fetchData().then((res) => {\n        // update the step details\n        update({ title: res.title })\n        // then show show the step\n        show()\n      })\n\n      return () => {\n        // cleanup fetch data\n      }\n    },\n  },\n]\n```\n\n### Wait Steps\n\nWait steps are useful when you need to wait for a specific condition before\nproceeding to the next step.\n\nUse the step `effect` function to perform an action and then call `next()` to\nmove to the next step.\n\n> **Note:** You cannot call `show()` in a wait step.\n\n```tsx\nconst steps: tour.StepDetails[] = [\n  {\n    id: \"step-1\",\n    type: \"wait\",\n    effect({ next }) {\n      const button = document.querySelector(\"#button\")\n      const listener = () => next()\n      button.addEventListener(\"click\", listener)\n      return () => button.removeEventListener(\"click\", listener)\n    },\n  },\n]\n```\n\n### Showing progress dots\n\nUse the `api.getProgressPercent()` to show the progress dots.\n\n```tsx\nconst ProgressBar = () => {\n  const service = useMachine(tour.machine, { steps: [] })\n  const api = tour.connect(service, normalizeProps)\n  return <div>{api.getProgressPercent()}</div>\n}\n```\n\n### Tracking the lifecycle\n\nAs the tour is progressed, events are fired and you can track the lifecycle of\nthe tour. Here's are the events you can listen to:\n\n- `onStepChange`: Fires when the current step changes.\n- `onStepsChange`: Fires when the steps array is updated.\n- `onStatusChange`: Fires when the status of the tour changes.\n\n```tsx\nconst Lifecycle = () => {\n  const service = useMachine(tour.machine, {\n    steps: [],\n    onStepChange(details) {\n      // => { stepId: \"step-1\", stepIndex: 0, totalSteps: 3, complete: false, progress: 0 }\n      console.log(details)\n    },\n    onStepsChange(details) {\n      // => { steps: StepDetails[] }\n      console.log(details.steps)\n    },\n    onStatusChange(details) {\n      // => { status: \"started\" | \"skipped\" | \"completed\" | \"dismissed\" | \"not-found\" }\n      console.log(details.status)\n    },\n  })\n\n  const api = tour.connect(service, normalizeProps)\n  // ...\n}\n```\n\n### Controlled current step\n\nUse `stepId` and `onStepChange` for controlled navigation.\n\n```tsx\nconst service = useMachine(tour.machine, {\n  steps,\n  stepId,\n  onStepChange(details) {\n    setStepId(details.stepId)\n  },\n})\n```\n\n### Programmatic tour control\n\nUse the connected API to drive tour flow from code.\n\n```tsx\napi.start()\napi.setStep(\"step-2\")\napi.next()\napi.prev()\napi.updateStep(\"step-2\", { title: \"Updated title\" })\n```\n\n### Dismiss and interaction behavior\n\nConfigure dismissal and page interaction behavior with machine props.\n\n```tsx\nconst service = useMachine(tour.machine, {\n  closeOnEscape: false,\n  closeOnInteractOutside: false,\n  preventInteraction: true,\n})\n```\n\n### Customizing progress text\n\nUse `translations.progressText` to customize the progress message.\n\n```tsx\nconst service = useMachine(tour.machine, {\n  translations: {\n    progressText: ({ current, total }) => `Step ${current} of ${total}`,\n  },\n})\n```\n\n## Styling guide\n\n### Prerequisites\n\nEnsure the `box-sizing` is set to `border-box` for the means of measuring the\ntour target.\n\n```css\n* {\n  box-sizing: border-box;\n}\n```\n\nEnsure the `body` has a `position` of `relative`.\n\n```css\nbody {\n  position: relative;\n}\n```\n\n### Overview\n\nEach tour part has a `data-part` attribute that can be used to style them in the\nDOM.\n\n```css\n[data-scope=\"tour\"][data-part=\"content\"] {\n  /* styles for the content part */\n}\n\n[data-scope=\"tour\"][data-part=\"positioner\"] {\n  /* styles for the positioner part */\n}\n\n[data-scope=\"tour\"][data-part=\"arrow\"] {\n  /* styles for the arrow part */\n}\n\n[data-scope=\"tour\"][data-part=\"title\"] {\n  /* styles for the title part */\n}\n\n[data-scope=\"tour\"][data-part=\"description\"] {\n  /* styles for the description part */\n}\n\n[data-scope=\"tour\"][data-part=\"progress-text\"] {\n  /* styles for the progress text part */\n}\n\n[data-scope=\"tour\"][data-part=\"action-trigger\"] {\n  /* styles for the action trigger part */\n}\n\n[data-scope=\"tour\"][data-part=\"backdrop\"] {\n  /* styles for the backdrop part */\n}\n```\n\n### Step types\n\nThe tour component can render `tooltip`, `dialog`, or `floating` content. You\ncan apply specific styles based on the rendered type:\n\n```css\n[data-scope=\"tour\"][data-part=\"content\"][data-type=\"dialog\"] {\n  /* styles for content when step is dialog type */\n}\n\n[data-scope=\"tour\"][data-part=\"content\"][data-type=\"floating\"] {\n  /* styles for content when step is floating type */\n}\n\n[data-scope=\"tour\"][data-part=\"content\"][data-type=\"tooltip\"] {\n  /* styles for content when step is tooltip type */\n}\n\n[data-scope=\"tour\"][data-part=\"positioner\"][data-type=\"dialog\"] {\n  /* styles for positioner when step is dialog type */\n}\n\n[data-scope=\"tour\"][data-part=\"positioner\"][data-type=\"floating\"] {\n  /* styles for positioner when step is floating type */\n}\n```\n\n### Placement Styles\n\nFor floating type tours, you can style based on the placement using the\n`data-placement` attribute:\n\n```css\n[data-scope=\"tour\"][data-part=\"positioner\"][data-type=\"floating\"][data-placement*=\"bottom\"] {\n  /* styles for bottom placement */\n}\n\n[data-scope=\"tour\"][data-part=\"positioner\"][data-type=\"floating\"][data-placement*=\"top\"] {\n  /* styles for top placement */\n}\n\n[data-scope=\"tour\"][data-part=\"positioner\"][data-type=\"floating\"][data-placement*=\"start\"] {\n  /* styles for start placement */\n}\n\n[data-scope=\"tour\"][data-part=\"positioner\"][data-type=\"floating\"][data-placement*=\"end\"] {\n  /* styles for end placement */\n}\n```\n\n## Methods and Properties\n\n### Machine Context\n\nThe tour machine exposes the following context properties:\n\n**`ids`**\nType: `Partial<{ content: string; title: string; description: string; positioner: string; backdrop: string; arrow: string; }>`\nDescription: The ids of the elements in the tour. Useful for composition.\n\n**`steps`**\nType: `StepDetails[]`\nDescription: The steps of the tour\n\n**`stepId`**\nType: `string`\nDescription: The id of the currently highlighted step\n\n**`onStepChange`**\nType: `(details: StepChangeDetails) => void`\nDescription: Callback when the highlighted step changes\n\n**`onStepsChange`**\nType: `(details: StepsChangeDetails) => void`\nDescription: Callback when the steps change\n\n**`onStatusChange`**\nType: `(details: StatusChangeDetails) => void`\nDescription: Callback when the tour is opened or closed\n\n**`closeOnInteractOutside`**\nType: `boolean`\nDescription: Whether to close the tour when the user clicks outside the tour\n\n**`closeOnEscape`**\nType: `boolean`\nDescription: Whether to close the tour when the user presses the escape key\n\n**`keyboardNavigation`**\nType: `boolean`\nDescription: Whether to allow keyboard navigation (right/left arrow keys to navigate between steps)\n\n**`preventInteraction`**\nType: `boolean`\nDescription: Prevents interaction with the rest of the page while the tour is open\n\n**`spotlightOffset`**\nType: `Point`\nDescription: The offsets to apply to the spotlight\n\n**`spotlightRadius`**\nType: `number`\nDescription: The radius of the spotlight clip path\n\n**`translations`**\nType: `IntlTranslations`\nDescription: The translations for the tour\n\n**`dir`**\nType: `\"ltr\" | \"rtl\"`\nDescription: The document's text/writing direction.\n\n**`id`**\nType: `string`\nDescription: The unique identifier of the machine.\n\n**`getRootNode`**\nType: `() => ShadowRoot | Node | Document`\nDescription: A root node to correctly resolve document in custom environments. E.x.: Iframes, Electron.\n\n**`onPointerDownOutside`**\nType: `(event: PointerDownOutsideEvent) => void`\nDescription: Function called when the pointer is pressed down outside the component\n\n**`onFocusOutside`**\nType: `(event: FocusOutsideEvent) => void`\nDescription: Function called when the focus is moved outside the component\n\n**`onInteractOutside`**\nType: `(event: InteractOutsideEvent) => void`\nDescription: Function called when an interaction happens outside the component\n\n### Machine API\n\nThe tour `api` exposes the following methods:\n\n**`open`**\nType: `boolean`\nDescription: Whether the tour is open\n\n**`totalSteps`**\nType: `number`\nDescription: The total number of steps\n\n**`stepIndex`**\nType: `number`\nDescription: The index of the current step\n\n**`step`**\nType: `StepDetails`\nDescription: The current step details\n\n**`hasNextStep`**\nType: `boolean`\nDescription: Whether there is a next step\n\n**`hasPrevStep`**\nType: `boolean`\nDescription: Whether there is a previous step\n\n**`firstStep`**\nType: `boolean`\nDescription: Whether the current step is the first step\n\n**`lastStep`**\nType: `boolean`\nDescription: Whether the current step is the last step\n\n**`addStep`**\nType: `(step: StepDetails) => void`\nDescription: Add a new step to the tour\n\n**`removeStep`**\nType: `(id: string) => void`\nDescription: Remove a step from the tour\n\n**`updateStep`**\nType: `(id: string, stepOverrides: Partial<StepDetails>) => void`\nDescription: Update a step in the tour with partial details\n\n**`setSteps`**\nType: `(steps: StepDetails[]) => void`\nDescription: Set the steps of the tour\n\n**`setStep`**\nType: `(id: string) => void`\nDescription: Set the current step of the tour\n\n**`start`**\nType: `(id?: string) => void`\nDescription: Start the tour at a specific step (or the first step if not provided)\n\n**`isValidStep`**\nType: `(id: string) => boolean`\nDescription: Check if a step is valid\n\n**`isCurrentStep`**\nType: `(id: string) => boolean`\nDescription: Check if a step is visible\n\n**`next`**\nType: `VoidFunction`\nDescription: Move to the next step\n\n**`prev`**\nType: `VoidFunction`\nDescription: Move to the previous step\n\n**`getProgressText`**\nType: `() => string`\nDescription: Returns the progress text\n\n**`getProgressPercent`**\nType: `() => number`\nDescription: Returns the progress percent","package":"@zag-js/tour","editUrl":"https://github.com/chakra-ui/zag/edit/main/website/data/components/tour.mdx"}