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

@hex-di/react

v0.3.0

Published

React integration for HexDI - type-safe hooks, Provider components, and automatic scope lifecycle management

Readme

@hex-di/react

React integration for HexDI - type-safe hooks, Provider components, automatic scope lifecycle management, and distributed tracing support.

Features

  • Type-Safe Hooks - Resolve services with compile-time validation
  • Provider Components - Container, Scope, and Tracing providers
  • Distributed Tracing - First-class tracing support via TracingProvider and hooks
  • Automatic Scope Lifecycle - Scopes tied to React component lifecycle
  • Factory Pattern - Isolated integrations for type safety
  • SSR Compatible - No global state, works with Next.js/Remix

Installation

pnpm add @hex-di/react react

Quick Start

import { port } from "@hex-di/core";
import { createContainer } from "@hex-di/runtime";
import { createTypedHooks } from "@hex-di/react";

// Define ports
const LoggerPort = port<Logger>()({ name: "Logger" });
type AppPorts = typeof LoggerPort;

// Create typed React integration
const { ContainerProvider, usePort } = createTypedHooks<AppPorts>();

// Use in your React app
function App() {
  return (
    <ContainerProvider container={container}>
      <MyComponent />
    </ContainerProvider>
  );
}

function MyComponent() {
  const logger = usePort(LoggerPort); // Type-safe!
  return <div>{logger.name}</div>;
}

Distributed Tracing

The TracingProvider integrates distributed tracing into React applications, enabling span creation for component lifecycle events, user interactions, and async operations.

TracingProvider Setup

Wrap your app root with TracingProvider to make tracing available to all components:

import { createMemoryTracer } from "@hex-di/tracing";
import { TracingProvider } from "@hex-di/react";

const tracer = createMemoryTracer();

function App() {
  return (
    <TracingProvider tracer={tracer}>
      <MyComponent />
    </TracingProvider>
  );
}

Integration with ContainerProvider

Combine tracing with DI container for full observability:

import { createContainer } from "@hex-di/runtime";
import { instrumentContainer, createConsoleTracer } from "@hex-di/tracing";
import { HexDiContainerProvider, TracingProvider } from "@hex-di/react";

const tracer = createConsoleTracer({ colorize: true });
const container = createContainer({ graph, name: "App" });

// Instrument container to trace all service resolutions
instrumentContainer(container, tracer, {
  portFilter: "UserService", // Only trace specific services
});

function App() {
  return (
    <TracingProvider tracer={tracer}>
      <HexDiContainerProvider container={container}>
        <Dashboard />
      </HexDiContainerProvider>
    </TracingProvider>
  );
}

useTracer Hook

Access the tracer instance for manual span management:

import { useTracer } from "@hex-di/react";

function DataLoader() {
  const tracer = useTracer();

  useEffect(() => {
    tracer.withSpan("component.mount", (span) => {
      span.setAttribute("component", "DataLoader");
    });
  }, [tracer]);

  return <div>Loading data...</div>;
}

When to use:

  • Manual span lifecycle control
  • Creating spans in useEffect or useLayoutEffect
  • Complex tracing logic with multiple nested spans

useSpan Hook

Get the currently active span for conditional logic or adding attributes:

import { useSpan } from "@hex-di/react";

function UserProfile({ userId }: { userId: string }) {
  const span = useSpan();

  // Add user context to active span if present
  if (span) {
    span.setAttribute("user.id", userId);
    span.setAttribute("component", "UserProfile");
  }

  return <div>Profile for {userId}</div>;
}

When to use:

  • Conditional tracing logic
  • Adding attributes to parent spans
  • Checking if code is running in a traced context

Returns: Span | undefined (undefined is valid when no span is active)

useTracedCallback Hook

Create callbacks wrapped in spans with automatic lifecycle management:

import { useTracedCallback } from "@hex-di/react";

function SaveButton() {
  const handleSave = useTracedCallback(
    "button.save.click",
    async () => {
      await saveData();
      // Span automatically created and ended
      // Errors automatically recorded
    },
    []
  );

  return <button onClick={handleSave}>Save</button>;
}

When to use:

  • Event handlers (onClick, onSubmit, onChange)
  • User interactions that should be traced
  • Async callbacks with automatic error handling

Features:

  • Works with both sync and async callbacks
  • Errors automatically recorded before re-throwing
  • Preserves callback signature and return type
  • Follows React hooks rules (same as useCallback)

Complete Tracing Example

Full integration with event handlers, attributes, and error handling:

import { useState } from "react";
import { TracingProvider, useTracedCallback, useSpan } from "@hex-di/react";
import { createConsoleTracer } from "@hex-di/tracing";

const tracer = createConsoleTracer({ colorize: true });

function App() {
  return (
    <TracingProvider tracer={tracer}>
      <TaskForm />
    </TracingProvider>
  );
}

function TaskForm() {
  const [title, setTitle] = useState("");
  const span = useSpan();

  // Add form context to active span
  if (span) {
    span.setAttribute("form.name", "TaskForm");
  }

  const handleSubmit = useTracedCallback(
    "form.task.submit",
    async (event: React.FormEvent) => {
      event.preventDefault();

      if (!title.trim()) {
        throw new Error("Title is required");
      }

      // API call automatically traced within span
      await createTask({ title });

      setTitle("");
    },
    [title]
  );

  const handleCancel = useTracedCallback(
    "button.cancel.click",
    () => {
      setTitle("");
    },
    []
  );

  return (
    <form onSubmit={handleSubmit}>
      <input
        type="text"
        value={title}
        onChange={(e) => setTitle(e.target.value)}
        placeholder="Task title"
      />
      <button type="submit">Create Task</button>
      <button type="button" onClick={handleCancel}>
        Cancel
      </button>
    </form>
  );
}

Error Handling in Traced Callbacks

Errors are automatically recorded to spans:

function DataFetcher() {
  const fetchData = useTracedCallback(
    "data.fetch",
    async () => {
      try {
        const response = await fetch("/api/data");
        if (!response.ok) {
          throw new Error(`HTTP ${response.status}`);
        }
        return await response.json();
      } catch (error) {
        // Error automatically recorded to span before re-throwing
        // span.recordException(error)
        // span.setStatus('error')
        throw error;
      }
    },
    []
  );

  return <button onClick={fetchData}>Fetch Data</button>;
}

Testing with TracingProvider

Use createMemoryTracer in tests to verify tracing behavior:

import { render, fireEvent, screen } from "@testing-library/react";
import { createMemoryTracer } from "@hex-di/tracing";
import { assertSpanExists, hasAttribute } from "@hex-di/tracing/testing";
import { TracingProvider } from "@hex-di/react";

it("traces button clicks", () => {
  const tracer = createMemoryTracer();

  render(
    <TracingProvider tracer={tracer}>
      <SaveButton />
    </TracingProvider>
  );

  fireEvent.click(screen.getByText("Save"));

  const spans = tracer.getCollectedSpans();
  assertSpanExists(spans, { name: "button.save.click" });

  const span = spans[0];
  expect(hasAttribute(span, "component", "SaveButton")).toBe(true);
});

Hook Summary

| Hook | Purpose | Returns | When to Use | | ------------------- | ------------------------------ | ---------------------- | -------------------------------- | | useTracer() | Access tracer instance | Tracer | Manual span management | | useSpan() | Get active span | Span \| undefined | Add attributes, conditional flow | | useTracedCallback | Create traced callback wrapper | (...args) => TReturn | Event handlers, async operations |

Container Provider

Provide DI container to React tree:

import { HexDiContainerProvider } from "@hex-di/react";

<HexDiContainerProvider container={container}>
  <App />
</HexDiContainerProvider>;

Scope Management

Automatic Scope Lifecycle

Create scopes tied to component lifecycle:

import { HexDiAutoScopeProvider } from "@hex-di/react";

function UserPage() {
  return (
    <HexDiAutoScopeProvider>
      <UserProfile />
      <UserSettings />
    </HexDiAutoScopeProvider>
  );
}

Manual Scope Provider

Control scope lifecycle manually:

import { HexDiScopeProvider, useContainer } from "@hex-di/react";

function MyComponent() {
  const container = useContainer();
  const scope = container.createScope();

  return (
    <HexDiScopeProvider scope={scope}>
      <ChildComponent />
    </HexDiScopeProvider>
  );
}

Async Container Loading

Handle async container initialization with loading/error states:

import { HexDiAsyncContainerProvider } from "@hex-di/react";

function App() {
  return (
    <HexDiAsyncContainerProvider container={asyncContainer}>
      {/* Loading state */}
      <HexDiAsyncContainerProvider.Loading>
        <div>Initializing services...</div>
      </HexDiAsyncContainerProvider.Loading>

      {/* Error state */}
      <HexDiAsyncContainerProvider.Error>
        {(error) => <div>Failed to initialize: {error.message}</div>}
      </HexDiAsyncContainerProvider.Error>

      {/* Ready state */}
      <HexDiAsyncContainerProvider.Ready>
        <Dashboard />
      </HexDiAsyncContainerProvider.Ready>
    </HexDiAsyncContainerProvider>
  );
}

Lazy Container Loading

Defer child container loading until needed:

import { HexDiLazyContainerProvider } from "@hex-di/react";

const lazyFeature = rootContainer.createLazyChild(
  async () => import("./feature-graph"),
  { name: "Feature" }
);

function FeaturePage() {
  return (
    <HexDiLazyContainerProvider lazyContainer={lazyFeature}>
      <HexDiLazyContainerProvider.Loading>
        <div>Loading feature...</div>
      </HexDiLazyContainerProvider.Loading>

      <HexDiLazyContainerProvider.Error>
        {(error) => <div>Failed: {error.message}</div>}
      </HexDiLazyContainerProvider.Error>

      <HexDiLazyContainerProvider.Ready>
        <FeatureComponent />
      </HexDiLazyContainerProvider.Ready>
    </HexDiLazyContainerProvider>
  );
}

Type Safety

Use factory pattern for full type safety:

import { createTypedHooks } from "@hex-di/react";
import type { LoggerPort, UserServicePort } from "./ports";

type AppPorts = typeof LoggerPort | typeof UserServicePort;

const { ContainerProvider, usePort } = createTypedHooks<AppPorts>();

function UserList() {
  // Type-safe: TypeScript validates UserServicePort exists in AppPorts
  const userService = usePort(UserServicePort);
  return <div>{userService.list().length} users</div>;
}

Error Handling

Handle DI errors in React Error Boundaries:

import { MissingProviderError } from "@hex-di/react";

class ErrorBoundary extends React.Component {
  componentDidCatch(error) {
    if (error instanceof MissingProviderError) {
      console.error("Component used DI hook outside Provider");
    }
  }
}

License

MIT