VTTForge on npm GitHub →
on npm · Foundry v14+

Forge sheets,
not boilerplate.

An SDK for building FoundryVTT v14+ systems and modules. Typed data models, sheets that already know their tabs and drops, and a dev loop that reloads in place.

$ bun create vttforge
Read the docs Design System
Foundry target
v14+
Layer prefix
vttforge.*
Tests
600+ + a real module
License
MIT
Why this exists

Good modules die
at every Foundry version.

What breaks is the sheet plumbing, the registration boilerplate, the class that moved namespace. VTTForge holds that in one place, so when Foundry moves it, you update a dependency instead of rewriting a sheet. If this project ever goes quiet, the output is plain ES modules Foundry loads natively: delete it and your code is still your code.

Without VTTForge ~140 lines per sheet
character-sheet.mjs javascript
class CharacterSheet extends ActorSheetV2 {

  // 36 lines of identical drag-drop wiring
  #createDragDropHandlers() {
    return this.options.dragDrop.map(d => {
      d.callbacks = {
        dragstart: this._onDragStart.bind(this),
        drop:      this._onDrop.bind(this),
      };
      return new DragDrop(d);
    });
  }

  async _prepareContext(options) {
    const context = await super._prepareContext(options);
    context.tabs = {
      primary: this._prepareTabs("primary"),
      /* every group, every sheet… */
    };
    return context;
  }

  async _onDropItem(event, data) {
    const item = await fromUuid(data.uuid);
    if (!item) return;
    // type-switching, validation, transfer…
  }
}
With VTTForge 12 lines · same behavior
character-sheet.ts typescript
import { BaseActorSheet } from "@vttforge/core";

class CharacterSheet extends BaseActorSheet() {
  static PARTS = {
    main: { template: "systems/demo/sheet.hbs" },
  };
  static TABS = {
    primary: { tabs: ["abilities", "inventory", "bio"] },
  };
  static DRAG_DROP = [{ dragSelector: ".item" }];

  // fromUuid already resolved, just handle your domain.
  async onDropItem(item, event) {
    await this.actor.createEmbeddedDocuments("Item", [item.toObject()]);
  }
}
What you get

Used in a shipped module
before it lands here.

A published Foundry module runs every API before it lands in the SDK. Where Foundry already does something well, the SDK leaves it alone.

{ }

Declarative sheet bases

BaseActorSheet and BaseItemSheet handle drag-drop wiring, tab state, and fromUuid resolution. What is left to write is the rules of your game.

@vttforge/core
T

Schema-to-type inference

One defineSchema() drives both runtime validation and your TypeScript types via InferSchema<T>. There is no second declaration to keep in sync.

@vttforge/core
≡

Cascade-layer CSS

@vttforge/styles nests under Foundry's system layer with a vendored prefix, so your rules win without !important, and it inherits Theme V2.

@vttforge/styles
⟳

Migration runner

createMigrationRunner() runs versioned migrations once at world load, GM only. A failure stops at the last version that finished, so the next load retries from there.

@vttforge/core
⚡

Reload in place

Save a template and only the windows using it redraw. Save a stylesheet and the CSS swaps. No page reload, no lost scroll position.

@vttforge/cli · @vttforge/dev-module
↯

Stable error registry

Every runtime error carries a VTTF-NNNN code from an append-only registry. Codes never change across majors, so a log line from last year still resolves to a docs page.

@vttforge/core
Packages

Eight packages,
one monorepo.

Install what you need. @vttforge/core is enough for a system or a module; the rest layer on top. All on npm, with provenance.

packagewhat it doesstatus
@vttforge/core Runtime: registerSystem / registerModule, sheet and enricher registration, BaseTypeDataModel with inference, BaseActorSheet / BaseItemSheet / BaseDocumentSheet / BaseApplication, PackageConfig, createMigrationRunner, VttfError. For modules: registerSocket, moduleApi, convertSubTypes, inject on npm
@vttforge/cli vttforge init / dev / build / audit / migrate / lint: four templates, the symlink-and-watch dev loop, the release zip, the v14 audit, the v13-to-v14 codemod and a bundled linter on npm
create-vttforge bun create vttforge, the scaffolder as a create package on npm
@vttforge/vite-plugin The build contract: browser ESM, no hashing, class names kept, CSS bundled, manifest copied with version synced on npm
@vttforge/styles The Forge design system: DTCG tokens compiled to CSS, sheet primitives, opt-in themes over cascade layers on npm
@vttforge/testing withMockFoundry for Vitest, which records sheets and enrichers, plus a Quench half for a real world on npm
@vttforge/dev-module The in-world half of the dev loop; vttforge dev installs it on npm
@vttforge/types The Foundry surface the bases stand on: ApplicationV2Members, DocumentSheetV2Members, VttforgeClass. Core re-exports them on npm
Try it today

Working example
in one command.

The repo ships a real Foundry v14 system and a module you can boot in Docker: tabbed character sheet, drag-drop inventory, demo migration, and a module that talks over a socket and adds a button to the sidebar. Requires Docker + a foundryvtt.com license.

Boot the example world

Clone the repo, fill in your Foundry license, run one compose command. The bundled examples/simple-system mounts read-only at localhost:30000.

terminal bash
# clone & install
git clone https://github.com/vttforge/vttforge
cd vttforge
bun install
bun run build

# fill in your foundryvtt.com license
cp .env.example .env

# boot Foundry v14 with the example system mounted
docker compose -f docker-compose.dev.yml up
# http://localhost:30000

Scaffold one in a minute

One command writes a system or a module you can open in Foundry: typed data models, a sheet with tabs and drag-drop, a migration runner, a release workflow. Then vttforge dev reloads templates and styles in place. The walkthrough is in getting started.

terminal bash
# a system, or a module
bun create vttforge my-system \
  --type system --lang ts

cd my-system

# link into Foundry, watch, reload in place
vttforge dev

# lint the manifest and source for v14 breakages
vttforge audit

# dist/ plus the release zip
vttforge build
Roadmap

What shipped,
and what is left before 1.0.

v0.0
Names reserved
v0.1
Core runtime · sheet bases · styles
v0.2
Vite plugin · dev loop · manifest sync
v0.3
CLI · four templates · audit · docs site
v0.4
Sockets · module api · sub-types · injection · decorators · typed Foundry surface
now
Other people's systems and modules, and the gaps they find
v1.0
Stable API · full inference

Build the next system
on VTTForge.

Every package is on npm. Scaffold one, read the guide, or file an issue with the boilerplate you keep writing by hand.