npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

react-attractor

v0.5.0

Published

React library to map DOM elements to React components

Readme

React Attractor

Enables you to retrieve React component information from DOM elements.

What is this?

A lightweight React library that enables you to retrieve React component information and custom metadata from DOM elements. Perfect for debugging, analytics, testing, and building developer tools.

Short example:

import { TetherProvider, withTether, useTetherContext } from 'react-attractor';

interface BoxMetadata {
  data: number;
}

// Apply tether ability on already declared component
const TetheredBox = withTether<BoxMetadata>(Box);

// Handle click events to retrieve component data
const handleClick = (event: React.MouseEvent) => {
  const { getTether } = useTetherContext<BoxMetadata>();
  const element = event.target as Element;
  const tether = getTether(element);
  if (tether) {
    console.log('Data:', tether.metadata?.data);
  }
};

const App = () => {
  return (
    <TetherProvider>
      <TetheredBox tetherMetadata={{ data: 123 }} />
      <TetheredBox tetherMetadata={{ data: 456 }} />
      <TetheredBox tetherMetadata={{ data: 789 }} />
    </TetherProvider>
  )
}

Installation

npm install react-attractor

Quick Start

Wrap your app with TetherProvider

TetherProvider manages tether information registered by components under its scope, enabling retrieval of component information from DOM elements. Multiple providers can be arranged hierarchically to enable scoped management.

import { TetherProvider } from 'react-attractor'

const App = () => {
  return (
    <TetherProvider>
      <YourApp />
    </TetherProvider>
  )
}

Enhance components with withTether

withTether is a higher-order component (HOC) that adds tether functionality to existing React components. Enhanced components become capable of retrieving component information and custom metadata from DOM elements.

import { withTether } from 'react-attractor';
import { forwardRef } from 'react';

// Apply tether ability already declared component
const TetheredBox = withTether(Box);

// For components that don't expose ref, add ref forwarding with forwardRef
const Box = forwardRef<HTMLDivElement>((props, ref) => (
  <div ref={ref} {...props} />
));

Note: Many UI libraries like Material UI already expose refs on most components, so you can apply withTether directly.

Use the enhanced component

Components enhanced with withTether gain a tetherMetadata property that allows you to set custom data associated with DOM elements. This metadata can be retrieved later from the DOM elements.

interface GreetingMetadata {
  userId: number;
  category: string;
}

const TetheredBox = withTether<GreetingMetadata>(Box);

const MyComponent = () => {
  return (
    <TetheredBox 
      label="Hello World" 
      color="#ff6b6b"
      tetherMetadata={{ userId: 123, category: 'greeting' }}
    />
  )
}

Retrieve component data from DOM elements

Once tethered components are rendered, you can retrieve component information and metadata from DOM elements. Use the getTether function from the useTetherContext hook to access component data from click events and other interactions.

import { useTetherContext } from 'react-attractor'

interface InspectorMetadata {
  userId?: number;
  category?: string;
  [key: string]: unknown;
}

function Inspector() {
  const { getTether } = useTetherContext<InspectorMetadata>()
  
  const handleClick = (event: React.MouseEvent) => {
    const element = event.target as Element
    const tether = getTether(element)
    
    if (tether) {
      console.log('Props:', tether.props)
      console.log('Custom metadata:', tether.metadata)
    }
  }
  
  return <div onClick={handleClick}>Click any tethered element</div>
}

Advanced Usage

Type-Safe Custom Metadata

Define custom metadata types for better type safety:

interface UserMetadata {
  userId: number
  permissions: ('read' | 'write' | 'delete')[]
  lastUpdated: Date
  department: string
}

const TetheredUserCard = withTether<UserMetadata>(UserCard)

// Usage with type checking
<TetheredUserCard 
  name="John Doe"
  tetherMetadata={{
    userId: 123,                    // Type checked
    permissions: ['read', 'write'], // Type checked
    lastUpdated: new Date(),        // Type checked
    department: 'Engineering'       // Type checked
    // invalidField: 'test'         // TypeScript error
  }}
/>

Retrieving Typed Metadata

interface UserMetadata {
  userId: number;
  permissions: ('read' | 'write' | 'delete')[];
  department: string;
}

const { getTether } = useTetherContext<UserMetadata>()

const handleElementInspection = (element: Element) => {
  const tether = getTether(element)
  
  if (tether?.metadata) {
    // Fully typed metadata access
    const userId = tether.metadata.userId           // number
    const permissions = tether.metadata.permissions // string[]
    const department = tether.metadata.department   // string
    
    // Type-safe business logic
    if (permissions.includes('write')) {
      enableEditMode(element)
    }
  }
}

Multiple Component Types

interface ButtonMetadata {
  action: string;
}

interface CardMetadata {
  userId: number;
}

const TetheredButton = withTether<ButtonMetadata>(Button)
const TetheredCard = withTether<CardMetadata>(Card)

// Each maintains its own props and metadata
<TetheredButton onClick={handleClick} tetherMetadata={{ action: 'submit' }}>
  Submit
</TetheredButton>

<TetheredCard title="User Profile" tetherMetadata={{ userId: 456 }}>
  Card content
</TetheredCard>

API Reference

TetherProvider

Provider component that manages tether registrations.

<TetherProvider>
  <App />
</TetherProvider>

withTether

Higher-order component that adds tether functionality to a component.

export const withTether<TMetadata = Record<string, any>>(
  WrappedComponent: ComponentType<any> | ForwardRefComponent<any>
): ComponentType<OriginalProps & TetherProps<TMetadata>>;

Requirements:

  • Component must accept and forward a ref to a DOM element
  • If the component doesn't expose ref, wrap it with React.forwardRef()

useTetherContext

Hook to access tether functionality.

const { getTether, registerTether } = useTetherContext();

Returns:

  • getTether(element: Element) - Retrieve tether data from DOM element
  • registerTether(element: Element, metadata: TetherMetadata) - Register tether data (used internally)

TetherMetadata

Interface for tether data structure.

interface TetherMetadata {
  component: ComponentType<any>;    // React component type
  props: Record<string, any>;       // Component props (excluding tetherMetadata)
  metadata?: any;                   // Custom metadata
}

TetherProps

Interface for enhanced component props.

interface TetherProps<TMetadata = Record<string, any>> {
  tetherMetadata?: TMetadata       // Custom metadata
}

License

MIT License - see LICENSE file for details.