TourKit
@tour-kit/hintsHeadless

HintHeadless

HeadlessHint: unstyled hint wrapper exposing visibility state and dismiss actions via render props for custom UIs

HintHeadless

The main headless hint component that manages state, positioning, and provides render props for complete UI customization.

Why Use HintHeadless?

  • Complete control - Build any hint UI you want
  • All logic included - State, positioning, dismissal handled
  • Render props - Access all internal state for custom rendering
  • Same accessibility - ARIA attributes built in

Basic Usage

import { HintHeadless } from '@tour-kit/hints/headless';

function CustomHint() {
  return (
    <HintHeadless
      id="welcome-hint"
      target="#welcome-button"
      content="Click here to get started!"
      render={({
        isOpen,
        targetRect,
        targetElement,
        show,
        hide,
        dismiss,
        hotspotRef,
        position,
        tooltipPlacement,
        content,
      }) => (
        <>
          {/* Custom hotspot */}
          <button
            ref={hotspotRef}
            onClick={isOpen ? hide : show}
            style={{
              position: 'fixed',
              top: targetRect.top - 4,
              left: targetRect.right - 4,
            }}
            className="w-4 h-4 bg-blue-500 rounded-full"
          />

          {/* Custom tooltip */}
          {isOpen && (
            <div
              style={{
                position: 'fixed',
                top: targetRect.bottom + 8,
                left: targetRect.left,
              }}
              className="p-3 bg-white shadow-lg rounded"
            >
              <p>{content}</p>
              <button onClick={dismiss}>Dismiss</button>
            </div>
          )}
        </>
      )}
    />
  );
}

Props

Prop

Type


Render Props

The render function receives these props:

Prop

Type


Position Options

// HotspotPosition values
type HotspotPosition =
  | 'top-left'
  | 'top-right'
  | 'bottom-left'
  | 'bottom-right'
  | 'center';
<HintHeadless
  id="hint"
  target="#element"
  position="bottom-right"  // Hotspot at bottom-right corner
  render={({ targetRect, position }) => {
    // position = 'bottom-right'
    // targetRect = { top, left, right, bottom, width, height }
  }}
/>

Complete Custom Hint

import { HintHeadless } from '@tour-kit/hints/headless';

function FeatureHint() {
  return (
    <HintHeadless
      id="new-export"
      target="#export-button"
      content="Export to multiple formats now available!"
      position="top-right"
      tooltipPlacement="right"
      persist={true}
      onDismiss={() => analytics.track('hint_dismissed')}
      render={({
        isOpen,
        targetRect,
        targetElement,
        show,
        hide,
        dismiss,
        content,
      }) => (
        <>
          {/* Custom pulsing hotspot */}
          <button
            onClick={isOpen ? hide : show}
            className="fixed z-50"
            style={{
              top: targetRect.top - 4,
              left: targetRect.right - 4,
            }}
          >
            <span className="flex h-4 w-4">
              <span className="animate-ping absolute h-4 w-4 rounded-full bg-blue-400 opacity-75" />
              <span className="relative rounded-full h-4 w-4 bg-blue-500" />
            </span>
          </button>

          {/* Custom tooltip */}
          {isOpen && (
            <div
              className="fixed z-50 p-4 bg-white rounded-lg shadow-xl"
              style={{
                top: targetRect.top,
                left: targetRect.right + 12,
              }}
            >
              <div className="flex items-start gap-2">
                <span className="text-xl">🆕</span>
                <div>
                  <p className="font-medium">{content}</p>
                  <div className="mt-2 flex gap-2">
                    <button
                      onClick={dismiss}
                      className="text-sm text-blue-600"
                    >
                      Got it
                    </button>
                    <button
                      onClick={hide}
                      className="text-sm text-gray-500"
                    >
                      Later
                    </button>
                  </div>
                </div>
              </div>
            </div>
          )}
        </>
      )}
    />
  );
}

Without Render Prop

For simple cases, use children instead:

<HintHeadless
  id="simple-hint"
  target="#button"
  className="w-4 h-4 bg-blue-500 rounded-full"
>
  {/* This renders when isOpen is true */}
  <div className="tooltip">
    <p>Hint content here</p>
  </div>
</HintHeadless>

When not using render, the component renders a basic button that toggles the tooltip. Use this for prototyping before implementing full custom rendering.


On this page