CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-tippyjs--react

React component wrapper for Tippy.js providing complete tooltip, popover, dropdown, and menu solution for the web

Pending
Overview
Eval results
Files

headless-rendering.mddocs/

Headless Rendering

Headless rendering provides complete control over tooltip appearance and behavior through a custom render function. This approach is ideal for CSS-in-JS libraries, design systems, and when you need full control over the tooltip's DOM structure and styling.

Capabilities

Basic Headless Tooltip

Creates a tooltip using a custom render function instead of built-in rendering.

/**
 * Headless Tippy component imported from @tippyjs/react/headless
 * @param props - TippyProps with render function
 * @returns React component with custom tooltip rendering
 */
declare const Tippy: React.ForwardRefExoticComponent<TippyProps>;

interface TippyProps {
  /** Custom render function for complete control over tooltip appearance */
  render?: (
    attrs: {
      'data-placement': Placement;
      'data-reference-hidden'?: string;
      'data-escaped'?: string;
    },
    content?: React.ReactNode,
    instance?: Instance
  ) => React.ReactNode;
  /** Child element to attach tooltip to */
  children?: React.ReactElement<any>;
  /** Tooltip content passed to render function */
  content?: React.ReactNode;
  /** Other Tippy.js props for positioning and behavior */
  [key: string]: any;
}

Usage Examples:

import React from 'react';
import Tippy from '@tippyjs/react/headless';

// Basic headless tooltip
function BasicHeadless() {
  return (
    <Tippy
      render={attrs => (
        <div className="custom-tooltip" tabIndex={-1} {...attrs}>
          My custom tooltip
        </div>
      )}
    >
      <button>Hover me</button>
    </Tippy>
  );
}

// With content prop
function HeadlessWithContent() {
  return (
    <Tippy
      content="Dynamic content"
      render={(attrs, content) => (
        <div className="tooltip-box" tabIndex={-1} {...attrs}>
          {content}
        </div>
      )}
    >
      <button>Hover me</button>
    </Tippy>
  );
}

Render Function Attributes

The render function receives positioning and state attributes that should be applied to your custom element.

interface RenderAttrs {
  /** Current placement of the tooltip */
  'data-placement': Placement;
  /** Present when reference element is hidden */
  'data-reference-hidden'?: string;
  /** Present when tooltip has escaped its boundary */
  'data-escaped'?: string;
}

Usage Examples:

// Conditional styling based on attributes
function ConditionalStyling() {
  return (
    <Tippy
      render={attrs => {
        const isHidden = attrs['data-reference-hidden'] !== undefined;
        const hasEscaped = attrs['data-escaped'] !== undefined;
        
        return (
          <div 
            className={`tooltip ${isHidden ? 'hidden' : ''} ${hasEscaped ? 'escaped' : ''}`}
            tabIndex={-1}
            {...attrs}
          >
            Conditional tooltip
          </div>
        );
      }}
    >
      <button>Hover me</button>
    </Tippy>
  );
}

CSS-in-JS Integration

Perfect integration with styled-components, emotion, and other CSS-in-JS libraries.

Usage Examples:

import React from 'react';
import Tippy from '@tippyjs/react/headless';
import styled from 'styled-components';

const TooltipBox = styled.div`
  background: #333;
  color: white;
  padding: 8px 12px;
  border-radius: 4px;
  font-size: 14px;
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
  
  &[data-placement^='top'] {
    margin-bottom: 8px;
  }
  
  &[data-placement^='bottom'] {
    margin-top: 8px;
  }
`;

function StyledTooltip() {
  return (
    <Tippy
      render={(attrs, content) => (
        <TooltipBox tabIndex={-1} {...attrs}>
          {content}
        </TooltipBox>
      )}
      content="Styled with CSS-in-JS"
    >
      <button>Styled tooltip</button>
    </Tippy>
  );
}

Animation Libraries

Integration with Framer Motion, React Spring, and other animation libraries.

Usage Examples:

import React from 'react';
import Tippy from '@tippyjs/react/headless';
import { motion } from 'framer-motion';

// Framer Motion animation
function AnimatedTooltip() {
  return (
    <Tippy
      render={(attrs, content) => (
        <motion.div
          tabIndex={-1}
          {...attrs}
          initial={{ opacity: 0, scale: 0.8 }}
          animate={{ opacity: 1, scale: 1 }}
          exit={{ opacity: 0, scale: 0.8 }}
          transition={{ duration: 0.2 }}
          style={{
            background: '#333',
            color: 'white',
            padding: '8px 12px',
            borderRadius: '4px',
          }}
        >
          {content}
        </motion.div>
      )}
      content="Animated tooltip"
      animation={false} // Disable built-in animation
    >
      <button>Animated tooltip</button>
    </Tippy>
  );
}

Custom Arrow

Create custom arrows with proper positioning using Popper.js attributes.

// Arrow must be an HTMLElement with data-popper-arrow attribute

Usage Examples:

import React from 'react';
import Tippy from '@tippyjs/react/headless';

// CSS arrow with data-popper-arrow
function CustomArrowTooltip() {
  return (
    <Tippy
      render={attrs => (
        <div className="tooltip-with-arrow" tabIndex={-1} {...attrs}>
          Custom tooltip content
          <div data-popper-arrow className="arrow" />
        </div>
      )}
    >
      <button>Arrow tooltip</button>
    </Tippy>
  );
}

// Ref-based arrow positioning
function RefArrowTooltip() {
  const [arrow, setArrow] = React.useState<HTMLDivElement | null>(null);

  return (
    <Tippy
      render={attrs => (
        <div className="tooltip" tabIndex={-1} {...attrs}>
          Content
          <div ref={setArrow} className="arrow" />
        </div>
      )}
      popperOptions={{
        modifiers: [
          {
            name: 'arrow',
            options: {
              element: arrow,
            },
          },
        ],
      }}
    >
      <button>Ref arrow</button>
    </Tippy>
  );
}

Instance Access

Access the Tippy instance for advanced control and method calls.

interface RenderFunction {
  (
    attrs: RenderAttrs,
    content?: React.ReactNode,
    instance?: Instance
  ): React.ReactNode;
}

Usage Examples:

// Instance methods and properties
function InstanceAccess() {
  return (
    <Tippy
      render={(attrs, content, instance) => (
        <div className="tooltip" tabIndex={-1} {...attrs}>
          <div>{content}</div>
          <button onClick={() => instance?.hide()}>
            Close
          </button>
        </div>
      )}
      content="Tooltip with close button"
      interactive={true}
    >
      <button>Interactive tooltip</button>
    </Tippy>
  );
}

Design System Integration

Perfect for integrating with existing design systems and component libraries.

Usage Examples:

import React from 'react';
import Tippy from '@tippyjs/react/headless';
import { Card, Text } from './design-system';

function DesignSystemTooltip() {
  return (
    <Tippy
      render={(attrs, content) => (
        <Card
          elevation="high"
          padding="sm"
          tabIndex={-1}
          {...attrs}
        >
          <Text size="sm">{content}</Text>
        </Card>
      )}
      content="Design system tooltip"
    >
      <button>Design system</button>
    </Tippy>
  );
}

Plugin Support

Headless rendering also supports Tippy.js plugins for extended functionality.

Usage Examples:

import React from 'react';
import Tippy from '@tippyjs/react/headless';
import { followCursor } from 'tippy.js/headless'; // Note: import from headless

function HeadlessWithPlugin() {
  return (
    <Tippy
      render={(attrs, content) => (
        <div className="custom-tooltip" tabIndex={-1} {...attrs}>
          {content}
        </div>
      )}
      content="I follow the cursor!"
      followCursor={true}
      plugins={[followCursor]}
    >
      <button>Headless with plugin</button>
    </Tippy>
  );
}

Note: When using headless rendering, import plugins from tippy.js/headless instead of tippy.js.

Import Path

Always import headless Tippy from the dedicated path:

import Tippy from '@tippyjs/react/headless'; // Correct
import Tippy from '@tippyjs/react'; // Wrong - this is default rendering

Key Considerations

  • Always include tabIndex={-1} on your custom tooltip element for accessibility
  • Spread the attrs object to ensure proper positioning and behavior
  • The className prop doesn't work in headless mode - apply styles directly to your custom element
  • Disable built-in animations with animation={false} when using custom animations
  • For SVG arrows, wrap them in a div with the data-popper-arrow attribute

Install with Tessl CLI

npx tessl i tessl/npm-tippyjs--react

docs

default-rendering.md

headless-rendering.md

index.md

singleton-mode.md

tile.json