react-tourlight

Beacons

Pulsing hotspots that let users opt into a tour or a "what's new" callout instead of being interrupted.

A beacon is a small pulsing dot anchored to an element. Clicking it starts a tour (or shows a single-element highlight). Use beacons when you'd rather invite users into a walkthrough than launch one on page load.

import { SpotlightBeacon, SpotlightProvider, SpotlightTour } from 'react-tourlight'
import 'react-tourlight/styles.css'

function App() {
  return (
    <SpotlightProvider>
      <SpotlightTour id="export-tour" steps={steps} />
      <SpotlightBeacon target="#export-button" tour="export-tour" />
      <Dashboard />
    </SpotlightProvider>
  )
}

Beacons:

  • render through a portal, so they're never clipped by overflow: hidden
  • follow the target across scroll, resize, and layout changes
  • wait for the target to appear (same MutationObserver logic as steps)
  • hide automatically while a tour or highlight is active
  • respect prefers-reduced-motion

Starting at a specific step

Deep-link a beacon into the middle of a tour with stepIndex:

<SpotlightBeacon target="#billing-tab" tour="onboarding" stepIndex={3} />

"What's new" callouts

Skip the tour entirely and show a one-off highlight when the beacon is clicked. The highlight's target defaults to the beacon's own target:

<SpotlightBeacon
  target="#export-button"
  position="top-left"
  highlight={{
    title: 'New: CSV export',
    content: 'Download any table as a CSV from the ⋯ menu.',
    placement: 'bottom',
  }}
/>

Positioning and styling

<SpotlightBeacon
  target={settingsRef}          // selector, ref, or () => element
  tour="settings"
  position="right"              // top-left | top-right | bottom-left | bottom-right
                                // top | bottom | left | right | center
  offset={6}                    // px, pushes the dot outward from the box
  size={14}                     // px diameter
  color="#f59e0b"               // any CSS color
  label="Show me around"        // aria-label
/>

The dot colour also reads the --spotlight-beacon-color CSS variable, so you can theme every beacon at once:

:root {
  --spotlight-beacon-color: #f59e0b;
}

Override the classes .spotlight-beacon and .spotlight-beacon-pulse for anything else.

Showing a beacon once

Beacons don't persist their own state — combine them with your own "seen" flag, or with tour persistence:

const [seen, setSeen] = useLocalStorage('export-tour-seen', false)

<SpotlightTour id="export-tour" steps={steps} onComplete={() => setSeen(true)} />
<SpotlightBeacon target="#export-button" tour="export-tour" active={!seen} />

Props

PropTypeDefaultDescription
targetSpotlightTarget--CSS selector, React ref, or resolver function
tourstring--Tour ID to start when clicked
stepIndexnumber0Step to start tour at
highlightOmit<SpotlightStep, 'target'> & { target?: SpotlightTarget }--Highlight to show when clicked (used when tour is not set)
onClick() => void--Extra click handler, runs before starting the tour / highlight
activebooleantrueRender the beacon
hideWhileActivebooleantrueHide while any tour or highlight is running
positionBeaconPosition'top-right'Anchor point on the target's bounding box
offsetnumber0Pixel offset pushing the dot outward
sizenumber12Dot diameter in px
colorstring--spotlight-beacon-color / #3b82f6Dot colour
labelstring'Start tour'Accessible label
classNamestring--Extra class names on the button
containerHTMLElement | (() => HTMLElement | null) | nulldocument.bodyPortal container
timeoutnumber5000Max time (ms) to wait for the target