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

minecraft-inventory

v0.1.6

Published

A flexible, scalable React library for rendering Minecraft inventory GUIs. Designed for integration into real Minecraft clients (e.g., mineflayer bots, web clients) without using CSS `transform: scale` — all sizing is driven by CSS custom properties.

Readme

minecraft-inventory

A flexible, scalable React library for rendering Minecraft inventory GUIs. Designed for integration into real Minecraft clients (e.g., mineflayer bots, web clients) without using CSS transform: scale — all sizing is driven by CSS custom properties.

Features

  • All standard inventory types (chest, furnace, crafting table, villager trades, enchanting table, brewing stand, anvil, smithing table, horse, beacon, and more)
  • CSS variable–based scaling — no layout jank, no transform: scale
  • <img>-rendered item textures with automatic items/blocks/ fallback (via PrismarineJS asset mirror by default)
  • Tooltips that follow the cursor, matching the original Minecraft style
  • Full keyboard support: number keys (1-9) to swap hotbar, Q to drop, double-click to collect, scroll wheel to pick/place
  • Mobile support: tap to open a context menu (take all / half / custom amount / drop)
  • Optional JEI (item browser) panel on the left or right
  • Bot connector layer — plugs into a mineflayer bot or any custom server connection
  • Demo connector with action logging for local development
  • Extendable registry — add new inventory types in one place

Quick Start (React)

import React from 'react'
import { createRoot } from 'react-dom/client'
import { InventoryGUI, createDemoConnector } from 'minecraft-inventory'

const connector = createDemoConnector({
  windowType: 'chest',
  windowTitle: 'My Chest',
  slots: [
    { index: 0, item: { type: 265, name: 'iron_ingot', count: 32, displayName: 'Iron Ingot' } },
    // ...more slots
  ],
})

function App() {
  return (
    <InventoryGUI
      type="chest"
      connector={connector}
      scale={2}
      showJEI
      jeiItems={[
        { type: 1, name: 'stone', displayName: 'Stone', count: 1 },
        { type: 4, name: 'cobblestone', displayName: 'Cobblestone', count: 1 },
      ]}
    />
  )
}

// Mount into the DOM
createRoot(document.getElementById('root')!).render(<App />)

Quick Start (No React — programmatic)

If you don't use React, use the mountInventory helper:

<div id="inventory"></div>
<script type="module">
  import { mountInventory, createDemoConnector } from 'minecraft-inventory'

  const connector = createDemoConnector({
    windowType: 'chest',
    windowTitle: 'My Chest',
    slots: [
      { index: 0, item: { type: 265, name: 'iron_ingot', count: 32, displayName: 'Iron Ingot' } },
    ],
  })

  const inv = mountInventory(document.getElementById('inventory'), {
    type: 'chest',
    connector,
    scale: 2,
  })

  // Update props later
  inv.update({ scale: 3 })

  // Destroy when done
  inv.destroy()
</script>

Installation

npm install minecraft-inventory
# or
pnpm add minecraft-inventory

Core API

<InventoryGUI> — All-in-one component

<InventoryGUI
  type="chest"                    // Inventory type name (see registry)
  title="My Chest"                // Override window title
  slots={[...]}                   // Optional: provide slots directly
  properties={{ litTime: 100 }}   // Optional: progress bar data
  connector={connector}           // Optional: bot connector
  scale={2}                       // Scale multiplier (default: 2)
  showJEI={false}                 // Show item browser panel
  jeiItems={[...]}                // Items to show in JEI
  jeiPosition="right"             // 'left' | 'right'
  textureBaseUrl="/assets/mc"     // Override texture base URL
  enableKeyboardShortcuts={true}
  onClose={() => {}}
/>

<InventoryOverlay> — Full-screen overlay with backdrop

Renders the inventory centered on screen with a semi-transparent backdrop. Clicking outside drops the held item or closes the window.

import { InventoryOverlay, InventoryProvider, ScaleProvider, TextureProvider } from 'minecraft-inventory'

<TextureProvider>
  <ScaleProvider scale={2}>
    <InventoryProvider connector={connector}>
      <InventoryOverlay
        type="chest"
        showBackdrop            // default: true (50% black)
        backdropColor="rgba(0,0,0,0.5)"
        showJEI
        jeiItems={items}
        jeiPosition="right"
        onClose={() => console.log('closed')}
      >
        {/* Extra children (e.g. notes panel) are rendered inside the centered anchor */}
      </InventoryOverlay>
    </InventoryProvider>
  </ScaleProvider>
</TextureProvider>

Manual composition

For full control, use the provider components and compose manually:

import {
  TextureProvider, ScaleProvider, InventoryProvider,
  InventoryWindow, Hotbar, HUD, CursorItem, JEI,
} from 'minecraft-inventory'

function MyInventory({ connector }) {
  return (
    <TextureProvider baseUrl="https://example.com/assets">
      <ScaleProvider scale={2}>
        <InventoryProvider connector={connector}>
          <div style={{ display: 'flex', gap: 8 }}>
            <InventoryWindow type="furnace" />
            <JEI items={allItems} position="right" />
          </div>
          <Hotbar />
          <HUD />
          <CursorItem />
        </InventoryProvider>
      </ScaleProvider>
    </TextureProvider>
  )
}

Scaling

Scale is driven entirely by CSS custom properties — no transform: scale. Changing the scale prop recalculates all size tokens:

| Variable | Default (scale=2) | |---|---| | --mc-scale | 2 | | --mc-slot-size | 36px (18 × scale) | | --mc-font-size | 14px (7 × scale) | | --mc-pixel | 2px |

// Changing scale re-renders the whole GUI at new dimensions
<InventoryGUI type="chest" scale={3} />

Textures

By default, item textures are fetched from the PrismarineJS Minecraft assets mirror on GitHub:

https://raw.githubusercontent.com/PrismarineJS/minecraft-assets/master/data/1.21.4/item/{name}.png

Provide name on each ItemStack to use the correct asset:

{ type: 276, name: 'diamond_sword', count: 1, displayName: 'Diamond Sword' }

To use local textures (e.g., from a resource pack server):

<InventoryGUI
  type="chest"
  textureBaseUrl="/assets/minecraft"
  // → /assets/minecraft/item/diamond_sword.png
/>

For full control, pass a custom texture config overriding any or all URL resolvers:

import { TextureProvider } from 'minecraft-inventory'

// Option A — simple base URL (all textures served from your CDN/server)
<TextureProvider baseUrl="https://yourserver.com/mc-assets/1.21.4">
  {/* item textures: /mc-assets/1.21.4/items/{name}.png           */}
  {/* block textures: /mc-assets/1.21.4/blocks/{name}.png         */}
  {/* GUI textures: /mc-assets/1.21.4/textures/{path}.png         */}
  <InventoryWindow type="chest" />
</TextureProvider>

// Option B — replace individual resolvers (config is merged with defaults)
<TextureProvider config={{
  getItemTextureUrl: ({ type, name }) =>
    `https://cdn.example.com/items/${name ?? type}.png`,

  getBlockTextureUrl: ({ type, name }) =>
    `https://cdn.example.com/blocks/${name ?? type}.png`,

  // version param lets you vary GUI textures per container (1.16.4, 1.21.4, etc.)
  getGuiTextureUrl: (path, version = '1.16.4') =>
    `https://cdn.example.com/gui/${version}/${path}.png`,
}}>
  <InventoryWindow type="furnace" />
</TextureProvider>

// Option C — completely static local assets (no CDN)
<TextureProvider config={{
  baseUrl: '/assets',
  getItemTextureUrl: ({ name, type }) => `/assets/items/${name ?? type}.png`,
  getBlockTextureUrl: ({ name, type }) => `/assets/blocks/${name ?? type}.png`,
  getGuiTextureUrl: (path) => `/assets/gui/${path}.png`,
}}>
  <InventoryWindow type="chest" />
</TextureProvider>

Bundled GUI textures (mc-assets) — Use the optional mc-assets package to bundle container backgrounds locally and avoid remote requests. Generate the texture map from your inventory registry, then pass the config to TextureProvider:

pnpm add mc-assets   # optional peer dependency
import { TextureProvider, InventoryOverlay } from 'minecraft-inventory'
import { localBundledTexturesConfig } from './bundledTexturesConfig'

// GUI container backgrounds (chest, furnace, etc.) load from bundled mc-assets;
// item/block textures still use the default remote URLs unless you override them.
<TextureProvider config={localBundledTexturesConfig}>
  <InventoryOverlay type="chest" ... />
</TextureProvider>

The generated file (src/generated/localTextures.ts) exports bundledTextureMap, allTexturePaths, and allContainerPaths. Use createBundledTexturesConfig({ remoteFallback: true, bundledTextureMap? }) to get a config with getGuiTextureUrl, setOverride(path, image), clearOverrides(), setRemoteFallback(enabled), and resetRenderedSlots(). Pass short paths to setOverride (e.g. gui/sprites/container/anvil/text_field_disabled.png) — the version prefix exists only for the texture import generator. Call resetRenderedSlots() after multiple setOverride calls to invalidate cached textures so slots re-request them. localBundledTexturesConfig is the default instance. Re-run pnpm gen:textures after changing inventory types.


Connector

The connector is the bridge between the GUI and a Minecraft server or bot.

Mineflayer connector

Use createMineflayerConnector(bot) to plug the GUI into a mineflayer bot. The connector turns slot clicks, drags, and drops into bot.clickWindow() (and plugin APIs for villager trades, enchantment table, anvil, beacon), and subscribes to mineflayer inventory events so the UI stays in sync.

Example — overlay when a container opens:

import React, { useState, useEffect } from 'react'
import { createRoot } from 'react-dom/client'
import mineflayer from 'mineflayer'
import {
  InventoryProvider,
  ScaleProvider,
  TextureProvider,
  InventoryOverlay,
  createMineflayerConnector,
} from 'minecraft-inventory'

const bot = mineflayer.createBot({
  host: 'localhost',
  port: 25565,
  username: 'InventoryViewer',
})

function App() {
  const [connector, setConnector] = useState(null)
  const [windowType, setWindowType] = useState(null)
  const [open, setOpen] = useState(false)

  useEffect(() => {
    const onOpen = () => {
      setConnector(createMineflayerConnector(bot))
      setWindowType(bot.currentWindow?.type ?? 'generic_9x1')
      setOpen(true)
    }
    const onClose = () => {
      setOpen(false)
      setConnector(null)
    }

    bot.on('windowOpen', onOpen)
    bot.on('windowClose', onClose)
    return () => {
      bot.removeListener('windowOpen', onOpen)
      bot.removeListener('windowClose', onClose)
    }
  }, [])

  if (!open || !connector || !windowType) return null

  return (
    <TextureProvider>
      <ScaleProvider scale={2}>
        <InventoryProvider connector={connector}>
          <InventoryOverlay
            type={windowType}
            onClose={() => bot.closeWindow(bot.currentWindow)}
            showJEI
            jeiItems={[]}
          />
        </InventoryProvider>
      </ScaleProvider>
    </TextureProvider>
  )
}

createRoot(document.getElementById('root')).render(<App />)

Hotbar “open inventory” button: If you render a hotbar with the container option (e.g. mobile open-inventory button), the connector handles the open-inventory action: it calls openPlayerInventory(), which opens the player inventory GUI, or the ridden entity’s inventory (e.g. llama) when mounted. No extra wiring needed once the connector is passed to InventoryProvider.

Demo connector (for local testing)

import { createDemoConnector } from 'minecraft-inventory'

const connector = createDemoConnector({
  windowType: 'crafting_table',
  windowTitle: 'Crafting',
  slots: [...],
  onAction: (entry) => {
    console.log(entry.description, entry.action)
  },
})

// Update slots programmatically:
connector.updateSlots(newSlots)
connector.setHeldItem({ type: 264, name: 'diamond', count: 1 })
connector.openWindow('furnace', 'Furnace', furnaceSlots)
connector.closeWindowExternal()

Custom connector

Implement the InventoryConnector interface for any other backend:

import type { InventoryConnector } from 'minecraft-inventory'

const myConnector: InventoryConnector = {
  getWindowState: () => ({ windowId: 1, type: 'chest', slots: [...], heldItem: null }),
  getPlayerState: () => null,
  sendAction: async (action) => {
    // send action to server
    console.log('action', action)
  },
  closeWindow: () => {},
  subscribe: (listener) => {
    // call listener({ type: 'windowUpdate', state }) on changes
    return () => {} // cleanup
  },
}

Keyboard Shortcuts (Desktop)

| Key | Action | |---|---| | Left click | Pick up all / place all | | Right click | Pick up half / place one | | Shift + Left click | Transfer to/from container | | Double click | Collect all of same item type | | 1–9 (while hovering) | Swap slot with hotbar slot N | | Q (while hovering) | Drop one item | | Ctrl+Q | Drop entire stack | | Scroll up | Pick up one more (right-click equivalent) | | Scroll down | Put one back | | Esc | Close window (onClose callback) |


Mobile Support

On touch devices, tapping a slot with no held item opens a context menu instead of immediately picking up the item. The menu appears to the side in landscape or below in portrait.

Context menu options:

  • Take All — picks up the entire stack
  • Take Half — picks up half
  • Take Amount…window.prompt to enter a custom quantity
  • Drop — drops the stack from the slot
  • Cancel

When you have a held item, tapping a slot places/transfers it (same as left-click on desktop).


JEI — Item Browser

import { JEI } from 'minecraft-inventory'

const items = [
  { type: 264, name: 'diamond', displayName: 'Diamond' },
  { type: 265, name: 'iron_ingot', displayName: 'Iron Ingot' },
  // ...
]

<JEI
  items={items}
  position="right"           // 'left' | 'right'
  onItemClick={(item) => {}} // left-click handler
  onItemMiddleClick={(item) => {}} // middle-click handler
/>

Use the search bar to filter by display name or item name. Scroll wheel or arrow buttons to paginate.


Adding New Inventory Types

All inventory types live in a single registry file:

src/registry/inventories.ts

Add a new entry to the inventoryDefinitions object:

export const inventoryDefinitions: Record<string, InventoryTypeDefinition> = {
  // ... existing types ...

  my_custom_chest: {
    name: 'my_custom_chest',
    title: 'Custom Chest',
    backgroundTexture: 'gui/container/my_custom_chest',  // path under textures/
    backgroundWidth: 176,
    backgroundHeight: 166,
    includesPlayerInventory: true,
    includesHotbar: true,
    slots: [
      // Custom container slots (indices 0-N)
      ...gridSlots(0, 9, 3, 8, 18, 'container'),
      // Player inventory (adjust indices to match server protocol)
      ...playerInv(84, 27, 54),
    ],
  },
}

Slot index conventions:

  • Container slots always start at 0
  • Player inventory and hotbar indices vary by window type (match the server protocol)
  • Use playerInv(yPos, invStartIndex, hotbarStartIndex) for the standard 3×9 + hotbar layout

Progress bars (for furnaces, brewing stands, etc.):

progressBars: [
  {
    id: 'cook_arrow',
    x: 79, y: 34, width: 24, height: 16,
    direction: 'right',            // 'right' | 'up' | 'down' | 'left'
    textureX: 176, textureY: 14,   // source coords in background texture
    getValue: (props) => props.cookingProgress ?? 0,
    getMax: (props) => props.totalCookTime || 200,
  },
],

Properties (data slots from server):

properties: {
  cookingProgress: { dataSlot: 2, description: 'Cook progress (0-200)' },
  totalCookTime: { dataSlot: 3, description: 'Total cook time' },
},

Registering at runtime (optional, for plugins/mods):

import { registerInventoryType } from 'minecraft-inventory'

registerInventoryType({
  name: 'my_mod_inventory',
  title: 'Mod Inventory',
  backgroundTexture: 'gui/container/my_mod',
  backgroundWidth: 176,
  backgroundHeight: 166,
  slots: [...],
})

Then use it anywhere:

<InventoryGUI type="my_mod_inventory" connector={connector} />

ItemStack Type

interface ItemStack {
  type: number        // Numeric item ID
  name?: string       // Snake_case name, e.g. 'diamond_sword' (used for texture URL)
  count: number
  metadata?: number
  nbt?: Record<string, unknown>
  displayName?: string
  enchantments?: Array<{ name: string; level: number }>
  lore?: string[]
  durability?: number        // Current durability value
  maxDurability?: number     // Max durability (renders bar when < max)
  textureKey?: string        // Override texture path for getItemTextureUrl
  texture?: string | HTMLImageElement  // Direct texture (bypasses URL lookup)
  blockTexture?: BlockTextureRender   // Isometric block icon from face slices
  debugKey?: string
}

Texture overrides (mineflayer itemMapper):

  • texture: string — URL or data URL, fetched and cached.
  • texture: HTMLImageElement — Preloaded image, used directly.
  • blockTexture: { source, top, left, right } — Composite an isometric block icon from three face slices. Each face has slice: [x, y, w, h] in source texture pixels. Uses a pool of aux canvases (not recreated per slot).

Project Structure

src/
  index.tsx                  # Library entry point / exports
  types.ts                   # Core TypeScript types
  registry/
    index.ts                 # registerInventoryType / getInventoryType
    inventories.ts           # All built-in inventory type definitions ← ADD NEW TYPES HERE
  components/
    InventoryWindow/         # Main window renderer
    Slot/                    # Individual slot (click/drag/keyboard/mobile)
    ItemCanvas/              # Canvas-based item texture rendering
    Tooltip/                 # Cursor-following item tooltip
    JEI/                     # Item browser panel
    Hotbar/                  # Hotbar with active slot indicator
    HUD/                     # XP bar
    CursorItem/              # Floating item following mouse cursor
  context/
    InventoryContext.tsx      # Shared state (held item, hover, drag)
    ScaleContext.tsx          # CSS variable scale provider
    TextureContext.tsx        # Texture URL resolver
  connector/
    types.ts                 # InventoryConnector interface
    mineflayer.ts            # Mineflayer bot adapter
    demo.ts                  # Demo connector with action log
  hooks/
    useMobile.ts
    useKeyboardShortcuts.ts
  styles/
    tokens.css               # CSS custom property definitions
playground/
  src/
    App.tsx                  # Interactive playground / demo
    mockItems.ts             # Sample items for testing

Contributing

Running the playground

pnpm install
pnpm dev
# Opens at http://localhost:3200

Adding a new inventory type

  1. Open src/registry/inventories.ts
  2. Add a new key to inventoryDefinitions (see template above)
  3. Test it in the playground by adding it to INVENTORY_TYPES in playground/src/App.tsx
  4. Submit a PR with the new type and sample mock slots in playground/src/mockItems.ts

Slot index reference

Each Minecraft window type has a fixed slot layout defined by the server protocol. The indices must match prismarine-windows / mineflayer slot numbering. Cross-reference with:

  • prismarine-windows for JS slot maps
  • Minecraft source: net/minecraft/world/inventory/ for the canonical layout

Memory leak note (Tooltip / MessageFormattedString)

If opening tooltips increases memory without returning to baseline, see docs/MEMORY_LEAK_ANALYSIS.md. The likely cause is filter:blur(2px) on obfuscated (§k) text creating retained compositor layers.

Connector protocol

When implementing a custom connector, actions have these shapes:

| Action type | Fields | |---|---| | click | slotIndex, button ('left'/'right'/'middle'), mode ('normal'/'shift'/'double'/'number'/'drop'), numberKey? | | drop | slotIndex, all (boolean) | | drag | slots (number[]), button | | trade | tradeIndex | | rename | text | | enchant | enchantIndex (0/1/2) | | beacon | primaryEffect, secondaryEffect | | hotbar-swap | slotIndex, hotbarSlot (0-8) | | close | — |


License

MIT