---
title: Maps
description: Give a client a filled map, convert RGB pixels, and update its image or markers.
---

A filled map has two parts: an item stack with a `map_id` component and a [MapItemData](/packets/play/mapitemdata) packet with the same ID. The item lets the player hold the map; the packet supplies its image and optional markers. Send them to a server-side `client` connection after it reaches the play state.

```ts
import { helpers } from "mc-protocol";

const mapId = 1;
const pixels = Array(helpers.MAP_SIZE * helpers.MAP_SIZE).fill("#ff0000");

await client.sendPacket("SetPlayerInventory", {
  slotId: 0, // first hotbar slot
  contents: helpers.createMapSlot(mapId, { name: "Red map" }),
});
await client.sendPacket("SetHeldSlot", { slot: 0 });
await client.sendPacket("MapItemData", helpers.createMapPacket(mapId, pixels, { format: "rgb" }));
```

`createMapSlot` and `createMapPacket` return plain packet data, not encoded buffers. `mapId` can be any integer from `0` to `2147483647`, but the same value must be used for the item and the image. `MAP_SIZE` is `128`.

## `createMapSlot(mapId, options?)`

Builds a `filled_map` [Slot](/docs/items) and sets its `map_id` component. It accepts the same `SlotOptions` as [`createSlot`](/docs/items#createslotitem-options), including `count`, `name`, `lore`, `glint`, and `components`. The supplied `mapId` wins over `components.map_id`.

```ts
helpers.createMapSlot(42, { name: "Spawn map", glint: true });
// A filled_map Slot with { type: "map_id", data: 42 } among its components.
```

Give the slot with [SetPlayerInventory](/packets/play/setplayerinventory), [ContainerSetSlot](/packets/play/containersetslot), or another packet that accepts a `Slot`. The helper does not send the item or the pixels for you. An invalid `mapId` throws `RangeError`; other slot options follow the [slot validation rules](/docs/items#validation).

## `createMapPacket(mapId, pixels, options?)`

Builds the payload for [MapItemData](/packets/play/mapitemdata). Without options, it sends a full `128 × 128` image with raw Minecraft palette indices. For a smaller region, set `x`, `y`, `width`, and `height`; `pixels` must contain exactly `width * height` entries.

| Option | Type | Default | Meaning |
| --- | --- | --- | --- |
| `format` | `"palette"` or `"rgb"` | `"palette"` | How to interpret `pixels` |
| `x` | integer, 0 to 127 | `0` | Left edge of the update region |
| `y` | integer, 0 to 127 | `0` | Top edge of the update region |
| `width` | integer, 1 to 128 | `128` | Number of columns to update |
| `height` | integer, 1 to 128 | `128` | Number of rows to update |
| `scale` | integer, 0 to 4 | `0` | Map scale sent in the packet |
| `locked` | `boolean` | `false` | Whether the map is locked |
| `icons` | `MapIcon[]` or `null` | `null` | Optional map markers; see [Markers](#markers) |

The region must fit inside the map: `x + width <= 128` and `y + height <= 128`. Pixels are ordered left to right on each row, then top to bottom. A `2 × 2` region needs four colors:

```text
[red, green,
 blue, white]
```

```ts
await client.sendPacket("MapItemData", helpers.createMapPacket(
  mapId,
  ["#ff0000", "#00ff00", "#0000ff", "#ffffff"],
  { format: "rgb", x: 20, y: 30, width: 2, height: 2 },
));
```

This changes only pixels `(20, 30)` through `(21, 31)`. To update one pixel, use `width: 1`, `height: 1`, and a one-element array.

### Palette pixels

In the default `format: "palette"`, pass a `Buffer`, `Uint8Array`, or array of byte values from `0` to `255`. Each value is one Minecraft map palette index; it is not an RGB channel or hex color. Indices `0` through `3` are transparent. The helper validates array values and copies the pixels into the returned packet's `data` buffer.

```ts
await client.sendPacket("MapItemData", helpers.createMapPacket(
  mapId,
  [4, 4, 4, 4],
  { x: 20, y: 30, width: 2, height: 2 },
));
```

The four `4` values fill the four pixels of a `2 × 2` square with the same palette color.

### RGB pixels

Set `format: "rgb"` to convert colors to the nearest Minecraft 26.2 map palette entries. An array contains one color per pixel, as `"#RRGGBB"` or a numeric `0xRRGGBB`. A `Buffer` or `Uint8Array` instead contains three bytes per pixel in `R, G, B` order:

```ts
helpers.createMapPacket(mapId, ["#ff0000", 0x00ff00], {
  format: "rgb", width: 2, height: 1,
});

helpers.createMapPacket(mapId, Buffer.from([255, 0, 0, 0, 255, 0]), {
  format: "rgb", width: 2, height: 1,
});
```

Both calls encode two pixels. RGB conversion chooses an opaque palette entry; it does not encode transparency or preserve arbitrary 24-bit colors. `rgbToMapColor(color)` exposes the same nearest-color lookup for one `"#RRGGBB"` or `0xRRGGBB` value:

```ts
helpers.rgbToMapColor("#ff0000"); // 18 in Minecraft 26.2
```

The helper does not decode PNG, JPEG, or other image files. Decode or resize an image to `128 × 128` RGB pixels first, then pass those pixels to `createMapPacket`.

### Validation

`createMapPacket` throws before sending when `mapId`, `scale`, or a region coordinate is outside its range; the region crosses the `128 × 128` boundary; the pixel count does not match the region; a palette array has a value outside `0` to `255`; or an RGB array contains a value outside `0x000000` to `0xffffff` or a string other than `"#RRGGBB"`. For RGB byte buffers, the required length is `width * height * 3`. It also checks `locked`, `icons`, and `format`.

## Markers

Pass `icons` to include map decorations in the packet. Each `MapIcon` has `type`, `x`, `z`, and `direction`, plus an optional `displayName` text value encoded as anonymous NBT. `x` and `z` are signed bytes on the map; `direction` is an unsigned byte. The helper copies the icon objects but does not validate their contents; the packet writer encodes them.

```ts
await client.sendPacket("MapItemData", helpers.createMapPacket(
  mapId,
  [4],
  {
    x: 0, y: 0, width: 1, height: 1,
    icons: [{ type: 0, x: 0, z: 0, direction: 0, displayName: "Spawn" }],
  },
));
```

`icons: null` omits the icon list; `icons: []` includes an empty list. For a marker update without pixels, send a raw [MapItemData packet](/packets/play/mapitemdata) with `columns: 0`; `createMapPacket` always builds an image region of at least one pixel.

## What you get back

```ts
helpers.createMapPacket(42, [4, 5], { x: 10, y: 20, width: 2, height: 1 });
// {
//   itemDamage: 42, scale: 0, locked: false, icons: null,
//   columns: 2, rows: 1, x: 10, y: 20,
//   data: Buffer.from([4, 5]),
// }
```

`itemDamage` is the wire field name for the map ID; it is not item durability. `columns` and `rows` are the region's width and height. `data` contains one palette byte per pixel, even when the input was RGB. The packet writer adds its length prefix.

## Types

All types are exported through `helpers`:

| Type | Shape |
| --- | --- |
| `MapPacketOptions` | Options in the table above |
| `MapItemDataPacket` | Returned packet shape shown above |
| `MapPixels` | `Buffer`, `Uint8Array`, or a readonly array of numbers or strings |
| `MapRgbColor` | `number` or `string` |
| `MapIcon` | `{ type: number; x: number; z: number; direction: number; displayName?: unknown }` |

```ts
import type { helpers } from "mc-protocol";

function sendMap(pixels: helpers.MapPixels) { /* … */ }
```
