Documentation menu

Guides

Lifecycle hooks

Last updated August 24, 2026

A hook is not a new kind of manifest entry. It is an ordinary [scripts] entry whose name is pre<something> or post<something>: Ember looks it up by name, runs it exactly the way embr run would, and skips it silently when it isn't defined. Nothing needs enabling, and there is no fixed list of hookable names.

A name only counts as a hook when something answers to the part after pre/post — either a command event (install, add, publish) or another entry in the same [scripts] table. So prelude is an ordinary script, and so is a prebuild in a manifest with no build: it can never fire on its own, and embr run prebuild is the only way it runs. Bare embr run groups them the same way it classifies them, so the listing tells you which of your entries are really hooks.

Scripts hook their own name#

Running build runs prebuild, then build, then postbuild:

[scripts]
prebuild  = "rm -rf build"
build     = "rojo build -o game.rbxl"
postbuild = "echo built"

That holds however the script was started — embr run build and the embr build shortcut behave the same.

Commands hook their verb#

embr install, embr add and embr publish run pre<verb> before their work and post<verb> after it:

[scripts]
preinstall  = "echo about to install"
postinstall = "embr build"                 # build once dependencies are in place
preadd      = "echo about to add"
postadd     = "echo added"
prepublish  = "embr build"                 # runs before the archive is packed
postpublish = "echo shipped"

Where exactly they sit:

  • preinstall runs before anything is resolved or downloaded; postinstall runs after the install completes.
  • preadd runs before the index prompt, so it can set up whatever resolving needs; postadd runs after ember.toml has been written.
  • prepublish runs before the archive is packed, which is the point of it — that is where you build what ships. postpublish runs only after a successful publish.

prepublish runs on --dry-run too, since a dry run exists to show the archive a real publish would produce.

Rules that apply to every hook#

  • Hooks never nest. prebuild is run as-is; Ember does not look for preprebuild. A hook is free to call embr run without recursion.
  • A hook is announced with the same two-line banner every script gets — it is an ordinary script, and it is the one nobody typed.
  • A failing hook stops the command. Ember exits with the hook's own exit code, after an error naming it: ✗ Script 'preinstall' failed with exit code 3. A post hook only runs if the work before it succeeded.

In a workspace#

Each manifest's hooks bracket its own portion, and run in its own directory. embr install at a workspace root goes:

root preinstall
root install
  member preinstall → member install → member postinstall   (per member)
root postinstall

So a root postinstall sees a fully installed workspace, and a member's own hooks see only that member. embr publish at the root has the same shape.