class

pub const MyClass = napi.class("MyClass", struct { ... });

Wrap a Zig struct as a JS class. The first argument is the JS-visible class name; the second is the Zig type.

pub const Counter = napi.class("Counter", struct {
    value: i32,

    pub fn init(start: i32) @This() {
        return .{ .value = start };
    }

    pub fn increment(self: *@This()) i32 {
        self.value += 1;
        return self.value;
    }

    pub fn deinit(self: *@This()) void {
        _ = self;
    }
});
const c = new Counter(10);
c.increment(); // 11

For the conceptual model, see Classes.

Recognized members

MemberRequiredSignaturePurpose
initYesfn(...args) T or fn(env: Env, ...args) TConstructor. Returns the struct value (or !T).
Mutating methodNofn(self: *Self, ...args) RBecomes a JS method.
Read-only methodNofn(self: *const Self, ...args) RBecomes a JS method.
nextNofn(self: *Self) ?ItemZig-style iterator: also adds [Symbol.iterator].
deinitNofn(self: *Self) voidRuns on JS GC.
Other pub fnNo(no *Self first param)Skipped silently.

init may also return !T to make construction fallible. Env is recognized in the constructor's first slot and any method's second slot, and does not consume a JS argument. The generated constructor requires new; a plain or receiver-bound call throws before init runs or native state is attached.

A next taking only self (plus optional Env) and returning an optional (or !?Item) makes instances iterable: they work with for..of, spread, and Array.from, and the generated .d.ts declares [Symbol.iterator](): IterableIterator<Item>. next is still exposed as a regular method. See Classes › Iterators.

Allocation

The instance is heap-allocated once (on std.heap.smp_allocator) during new and reused across every method call. Released automatically when JS collects the wrapper, after running deinit if defined. If construction initializes T but the wrapper cannot be installed, the bridge also runs deinit before discarding that temporary value.

Class receivers are tagged and checked before native memory is unwrapped. Borrowing a method onto an instance of another native class throws TypeError instead of reinterpreting the other allocation.

Method names must remain unique after camel-casing and must be directly writable as TypeScript method names. Reserved words such as await are valid methods, but constructor and names containing punctuation are rejected because they would make the generated class declaration ambiguous or invalid.

If your fields hold long-lived allocations (strings, slices, file handles), free them in deinit. The arena from env.allocator() is per-call only and is not suitable for instance state.