Installing & Requiring Packages on Roblox
This page covers how Roblox packages are installed, required, and mounted in your project. For the platform-neutral rules (version ranges, the lockfile, private access), see Concepts.
Prerequisites
Before you can install packages, you’ll need the Forest CLI installed.
Installing packages
Suppose your project needs three packages: forest/pasta, user1/sauce, and user1/meatballs. Install each with forest install (alias forest i; every flag is covered in the CLI reference):
forest i forest/pasta
forest i user1/sauce
forest i user1/meatballsThis downloads each package into your project’s Packages/ folder (next to forest.json) and records it in your manifest.
Requiring packages
Each installed package is a folder with an init module at its top, so in Roblox it is a ModuleScript. You require it by its instance path, from wherever the Packages folder lives in your datamodel:
local Packages = game:GetService("ReplicatedStorage").Packages
local Pasta = require(Packages.pasta)
local Sauce = require(Packages.sauce)
local Meatballs = require(Packages.meatballs)
-- Do something with our now-imported ingredientsThe folder name you require is the package’s name without its scope (forest/pasta becomes pasta), or the alias you chose at install time (forest i user2/pasta -a pasta2 becomes pasta2).
Whatever a package’s root module returns is its entire public API. See Package Anatomy.
Using packages this way keeps every part of your project modular. You can update, replace, or share each piece independently.
Where packages live
On the filesystem, Forest installs everything into one Packages/ folder next to your forest.json. Forest does not touch your place file or decide datamodel locations.
(If your forest.json has a root field, meaning you’re authoring a package rather than a game, the Packages/ folder lives next to the root module instead, e.g. src/Packages/. See Package Anatomy.)
In the Roblox datamodel, you choose where that folder is mounted. If you use Rojo , map it in your project file. ReplicatedStorage.Packages is the common choice:
{
"name": "my-game",
"tree": {
"$className": "DataModel",
"ReplicatedStorage": {
"Packages": { "$path": "Packages" }
}
}
}All requires inside the installed tree are relative (script.Parent-style), so the Packages folder works wherever you mount it: ReplicatedStorage, ServerScriptService, or anywhere else.
Forest has no realm concept, meaning there are no separate server/client dependency lists or install targets. There is one packages tree, and its location is your choice. So if you mount it under ReplicatedStorage, every package’s source is visible to clients, including packages you only use on the server. Don’t put secrets (API keys, webhook URLs, anti-cheat logic you want hidden) in package code. This is by design: every published public package’s code is meant to be visible to all consumers, so the exposure of source is expected and intentional.
Server-only packages: the two-manifest pattern
Sometimes the single tree isn’t what you want. A private package full of server logic, or an anti-cheat library, shouldn’t replicate to clients just because everything else lives in ReplicatedStorage.
Since every forest.json gets its own fully self-contained Packages/ tree, the supported way to split realms today is one manifest per realm. Run forest init --project in two folders and install each package where it belongs:
- forest.json
- forest-lock.json
- forest.json
- forest-lock.json
Then mount each tree on its own side of the datamodel:
{
"name": "my-game",
"tree": {
"$className": "DataModel",
"ServerScriptService": {
"Packages": { "$path": "Server/Packages" }
},
"ReplicatedStorage": {
"Packages": { "$path": "Shared/Packages" }
}
}
}Packages under ServerScriptService (or ServerStorage) never replicate, so their source is not visible to clients. Server code requires from ServerScriptService.Packages, shared/client code from ReplicatedStorage.Packages.
Things to know about this pattern:
- Run CLI commands from the right folder.
forest install,remove, and the lockfile are per-manifest, so installs happen inServer/orShared/depending on where the package belongs. Commit both lockfiles. - The trees are fully independent. A package needed in both realms is installed twice, once per tree, with separate state at runtime. For pure libraries this is harmless; each side only ever requires its own tree.
- This changes where code lives, not what it can do. Server-mounted packages are hidden from clients, but any installed package still runs with your game’s full permissions. Visibility and trust are separate questions.
Package Dependencies
When you install a package, Forest automatically installs any dependencies it requires.
For example, if forest/pasta depends on forest/flour, Forest installs both when you run:
forest i forest/pasta
After installation, your project’s file tree looks like this:
- script.lua
- init.lua
- LICENSE
- init.lua
- LICENSE
In the example file tree, the root Packages folder contains the three packages we installed. Inside the pasta package, there’s another Packages folder with its dependency, flour.
Since we didn’t install flour directly, it doesn’t appear in the root Packages folder. That means it isn’t available for you to require() in your project.
This structure keeps dependencies isolated. Each package manages its own requirements without affecting the main project or other packages. Forest generates small pointer modules (return require(script.Parent...)) so version-conflicted or transitive-only packages nested inside a dependent’s own Packages/ folder are always found at a predictable relative location. Every link is a relative instance path, which is why the whole tree can be mounted anywhere in the datamodel. If you do want to use flour directly, install it yourself with:
forest i forest/flour
For exactly how versions are chosen, how conflicts between shared dependencies resolve, and how the lockfile pins your tree, see Dependencies & Versioning.