Skip to content

Selectors

A Selector is a typed target expression - the @a, @e, @s, @p, @r you'd hand-write, but built as an object so every filter is checked and rendered version-aware. You never string together @a[distance=..6,tag=it] by hand.

Building one

Start from a base and chain filters. Every filter method returns this, so they read as one fluent expression, and the order you add them in doesn't matter:

Start fromBase
Selector.allPlayers()@a
Selector.allEntities()@e
Selector.self()@s
Selector.nearest()@p
Selector.random()@r
Selector.uuid(id)a bare UUID / player name

The filters cover the vanilla selector arguments as typed values - note distance, score, and volume take real Range, Objective, and corner tuples rather than string fragments:

MethodSelector arg
.tag(name)tag= (repeatable)
.limit(n) / .sort(order)limit= / sort=
.distance(range)distance= (a Range - new Range(undefined, 6) is ..6)
.score(objective, range)scores={…}
.gamemode(mode)gamemode=
.team(name) / .name(name)team= / name=
.nbt(nbt)nbt={…} (an Nbt value)
.predicate(ref)predicate= (repeatable)
.volume(from, to)x/y/z + dx/dy/dz from two corners
.xRotation(range) / .yRotation(range)x_rotation= / y_rotation=
ts
import { Datapack, v26_2, Selector, Range, Gamemode, Sort } from "helix";

const dp = new Datapack("targets", v26_2);
const game = dp.objective("game");

const fn = dp.createFunction("reward_nearby");
fn.build((ctx) => {
  const winners = Selector.allPlayers()
    .gamemode(Gamemode.SURVIVAL)
    .distance(new Range(undefined, 8))  // within 8 blocks: ..8
    .score(game, new Range(10, undefined))  // score >= 10
    .sort(Sort.NEAREST)
    .limit(3);

  ctx.tellraw(winners, "you're close enough to win");
});

▶ Open in playground

Compiled output - the real files helix emits for the code above:

txt
tellraw @a[scores={game=10..},distance=..8,gamemode=survival,limit=3,sort=nearest] {"text":"you're close enough to win"}

A Range is new Range(min, max) - either bound may be undefined for an open end (new Range(undefined, 8)..8, new Range(10, undefined)10.., new Range(3, 3) → the exact value 3).

Where selectors go

A Selector renders itself version-aware, so you pass the object straight into any command that takes a target - ctx.tellraw, ctx.playerGive, ctx.summon positioning, every execute clause. See Execute chains for .as/.at, and Scores for scoring a whole selector at once.

Running a body per match

selector.run(fn) wraps a body in execute as <selector> run … - the typed form of "do this once per matching entity", with @s bound to each match inside the callback:

ts
import { Datapack, v26_2, Selector } from "helix";

const dp = new Datapack("targets", v26_2);

const fn = dp.createFunction("tag_creepers");
fn.build((ctx) => {
  Selector.allEntities()
    .name("Creeper")
    .run((ctx) => {
      ctx.tellraw(Selector.self(), "you have been tagged");
    })(ctx);
});

▶ Open in playground

Compiled output - the real files helix emits for the code above:

txt
tellraw @e[name=Creeper] {"text":"you have been tagged"}

Cheap membership checks with predicates

.predicate(ref) is the engine-evaluated stand-in for inlining a big nbt={…} match into the selector: register the check once with dp.predicate(...), then reference it from any selector (repeatable to AND several). See Data resources for building predicates.

Released under the MIT License · Credits