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

inversify-hooks

v4.0.0

Published

Dependency injection for React with TypeScript hooks, powered by InversifyJS.

Readme

Inversify Hooks

Dependency injection for React + TypeScript, the easy way. inversify-hooks lets your components resolve services from an InversifyJS container through a single useInject hook — no providers, no boilerplate. It's a thin React layer over inversify-props, which is built on inversify 8.

npm version npm downloads GitHub license GitHub last commit GitHub issues

logo


Table of contents

Why

Sharing services across a React tree usually means prop-drilling or hand-rolled context. With inversify-hooks you register a class once and pull a fully-wired instance into any component:

const [userService] = useInject<IUserService>(cid.IUserService);

That's the whole API surface for a component. The container, scoping, and wiring are handled for you by InversifyJS underneath.

Installation

npm install inversify-hooks

inversify-props (and transitively inversify) is a regular dependency, pulled in for you; react (>= 16.8, for hooks) is the only peer dependency. TypeScript type definitions ship with the package, and both ESM and CommonJS builds are included. No separate reflect-metadata install is needed — inversify 8 no longer requires it.

Note: inversify 8 is ESM-first. Bundler users (Vite, Next, webpack, etc.) need nothing special. Consuming it from a plain CommonJS Node app via require() needs Node 20.19+ or 22+.

TypeScript configuration

Decorator support is required, or injection silently does nothing:

{
  "compilerOptions": {
    "target": "es2020",
    "lib": ["es2020", "dom"],
    "moduleResolution": "bundler",
    "experimentalDecorators": true,
    "useDefineForClassFields": false
  }
}

⚠️ Keep useDefineForClassFields false (the default when target is below ES2022). With ES class-field define semantics enabled, an instance field shadows the injected getter and the property comes back undefined. See Troubleshooting.

Quick start

// main.tsx — app entry point
import { createRoot } from 'react-dom/client';
import { container } from 'inversify-hooks';
import App from './App';
import { IUserService, UserService } from './services';

container.addSingleton<IUserService>(UserService);

createRoot(document.getElementById('root')!).render(<App />);
// App.tsx
import { cid, useInject } from 'inversify-hooks';
import { IUserService } from './services';

export default function App() {
  const [userService] = useInject<IUserService>(cid.IUserService);
  return <h1>Hello, {userService.getName()}</h1>;
}

Registering dependencies

Register on the shared container before the app renders. Three lifetimes are available:

import { container } from 'inversify-hooks';

container.addSingleton<IUserService>(UserService); // one shared instance (most common)
container.addTransient<ILogger>(Logger);           // a new instance on every resolve
container.addRequest<IUnitOfWork>(UnitOfWork);     // one instance per request scope

The generic (<IUserService>) is just the compile-time type. The runtime id is derived from the class name and cached under both UserService and IUserService, which is why cid.IUserService works. Need an explicit id? Pass one:

container.addSingleton<IUserService>(UserService, 'MyUserService');
const [userService] = useInject<IUserService>('MyUserService');

Injecting into components

useInject<T>(id) returns a one-element tuple (so you can name the variable on destructure):

function Profile() {
  const [userService] = useInject<IUserService>(cid.IUserService);

  useEffect(() => {
    userService.load();
  }, [userService]);

  return <span>{userService.getName()}</span>;
}

Injecting into other services

Services can depend on other services via @inject() property injection. The id is resolved from the property name, so name the property after the class you registered:

import { inject, injectable } from 'inversify-hooks';

@injectable()
export class OrderService implements IOrderService {
  @inject() private userService!: IUserService; // resolves the "UserService" registration

  placeOrder() {
    return this.userService.getName();
  }
}

Mark every injectable class with @injectable(). Property injection is lazy: the dependency is pulled from the container the first time it's accessed.

Testing with mocks

Swap a real implementation for a fake and reset between tests:

import { cid, mockSingleton, resetContainer } from 'inversify-hooks';

afterEach(() => resetContainer());

it('greets the user', () => {
  mockSingleton<IUserService>(cid.IUserService, FakeUserService);
  // render the component / call the service and assert
});

mockTransient and mockRequest have the same signature. resetContainer() unbinds everything.

API reference

| Export | Description | | --- | --- | | useInject<T>(id) | React hook. Returns [T] — the instance resolved for id. | | container | The shared container instance (auto-created). | | container.addSingleton<T>(Class, id?) | Register a single shared instance. | | container.addTransient<T>(Class, id?) | Register a new instance per resolve. | | container.addRequest<T>(Class, id?) | Register one instance per request scope. | | cid | Cache of generated ids, e.g. cid.IUserService. | | inject / Inject | Property/parameter decorator for injecting into services. | | injectable | Class decorator marking a class as injectable. | | mockSingleton / mockTransient / mockRequest | Replace a registered id with another implementation (testing). | | resetContainer() | Unbind everything from the container. | | getContainer() / setContainer(opts) | Access or replace the underlying InversifyJS container. | | Container | The container class, if you want your own instance. |

Runnable example

A complete React + Vite example lives in examples/react-example:

# from the repo root — build the library first
npm install && npm run build

cd examples/react-example
npm install
npm run dev

Use it as an agent skill

This repo ships an Agent Skill so AI coding agents (Claude Code, Cursor, etc.) know how to wire up DI with this library. Install it with npx skills:

npx skills add CKGrafico/inversify-hooks

Troubleshooting

| Symptom | Cause & fix | | --- | --- | | Injected property is undefined | useDefineForClassFields is true. Set it to false (or keep target below ES2022). | | Works in dev, breaks in production | The minifier mangled class names, so cid.IXxx is undefined. Enable keepNames (esbuild/Vite) or the equivalent Terser/Uglify setting. | | No matching bindings found | The service wasn't registered, or registration ran after the component resolved. Register before render. | | Decorators throw at runtime | experimentalDecorators is off in your tsconfig.json. |

Credits

A thin React layer over inversify-props, built on InversifyJS. Licensed under MIT.