Gleam Project Structure

Every Gleam project follows a predictable layout. Understanding this layout helps you find code quickly, organize new files correctly, and work with other Gleam developers without confusion.

The Standard Project Layout

When you run gleam new my_app, Gleam creates this structure:


my_app/
├── gleam.toml           ← project settings and dependencies
├── manifest.toml        ← locked dependency versions (auto-generated)
├── README.md            ← project description
├── src/
│   └── my_app.gleam     ← entry point (main function lives here)
└── test/
    └── my_app_test.gleam  ← tests for your code

After you compile the project, a new folder appears:


my_app/
├── build/               ← compiled output (never edit manually)
│   └── dev/
│       └── erlang/
│           └── my_app/
│               └── ebin/   ← compiled .beam files

You never touch the build/ folder. Gleam manages it automatically. Add it to your .gitignore so it does not clutter your version control history.

The gleam.toml File

This file is the heart of your project. It stores everything the Gleam CLI needs to understand your project.


gleam.toml — Anatomy
─────────────────────────────────────────────────────
name = "my_app"          ← project name (lowercase, underscores)
version = "1.0.0"        ← your project's version
target = "erlang"        ← compile target: erlang or javascript

[dependencies]           ← packages your project needs to run
gleam_stdlib = ">= 0.34.0 and < 2.0.0"

[dev-dependencies]       ← packages needed only for testing
gleeunit = ">= 1.0.0 and < 2.0.0"

The target field decides whether Gleam compiles to Erlang bytecode or JavaScript. For server applications, use erlang. For browser applications, use javascript. The default is erlang.

The manifest.toml File

When you add a dependency, Gleam resolves the exact version that fits all your requirements and writes it to manifest.toml. This file locks versions so every developer on your team runs identical code.


manifest.toml — Purpose
────────────────────────────────────────────
gleam.toml says:   "I need stdlib version >= 0.34.0"
manifest.toml says: "Use exactly version 0.36.0"

Result: Every developer gets 0.36.0 — no surprises.

Commit manifest.toml to version control. Do not commit the build/ folder.

The src/ Directory

All your application source code lives in src/. You can create multiple files here, and each file becomes a module.


src/
├── my_app.gleam       ← module name: my_app
├── user.gleam         ← module name: user
├── order.gleam        ← module name: order
└── utils/
    └── helpers.gleam  ← module name: utils/helpers

The module name matches the file path relative to src/. A file at src/utils/helpers.gleam becomes the module utils/helpers, which you import with:

import utils/helpers

The Entry Point

The file with the same name as your project (e.g., src/my_app.gleam) holds the main function. When you run gleam run, execution starts there.

import gleam/io

pub fn main() {
  io.println("App started!")
}

The test/ Directory

All test files live in test/. Gleam does not mix application code and test code.


test/
├── my_app_test.gleam   ← tests for my_app module
├── user_test.gleam     ← tests for user module
└── order_test.gleam    ← tests for order module

Test files use the same naming pattern: take the source file name, add _test at the end. This makes it obvious which tests belong to which module.


src/user.gleam         ← source module
test/user_test.gleam   ← test module for user

Organizing a Larger Project

Real applications have dozens of files. A well-organized Gleam project groups related files into subfolders inside src/.


Example: E-commerce App Structure
─────────────────────────────────────────────
src/
├── shop.gleam            ← entry point
├── product/
│   ├── catalog.gleam     ← product listing
│   └── search.gleam      ← search logic
├── order/
│   ├── cart.gleam        ← shopping cart
│   └── checkout.gleam    ← payment flow
└── user/
    ├── auth.gleam        ← login / signup
    └── profile.gleam     ← user settings

test/
├── product/
│   └── catalog_test.gleam
├── order/
│   └── cart_test.gleam
└── user/
    └── auth_test.gleam

Each folder groups a business concept. You can find any file immediately because the folder name describes what it contains.

Adding Source Files

Create a new file anywhere inside src/ and start writing. No registration or configuration is required. The Gleam compiler discovers all .gleam files in src/ automatically.


# Create a new file
touch src/product/catalog.gleam

# Write a function in it
pub fn list_all() {
  ["Laptop", "Keyboard", "Mouse"]
}

# Import it from another file
import product/catalog

pub fn main() {
  catalog.list_all()
}

Build Targets and Environments

Gleam builds your project in two modes:


Build Modes
────────────────────────────────────────────────
Mode         │ Command          │ Optimization
─────────────┼──────────────────┼───────────────
Development  │ gleam run        │ Fast compile, no opt
             │ gleam test       │
─────────────┼──────────────────┼───────────────
Production   │ gleam build      │ Optimized output

Development mode prioritizes fast feedback. Production mode creates the optimized build you deploy to a server.

The .gitignore Recommendation

Add these lines to your .gitignore file to keep your repository clean:

build/
*.beam

The build/ folder is large and reconstructible. Every developer regenerates it by running gleam build. Storing it in git wastes space and causes merge conflicts.

Project Structure at a Glance


Summary Diagram
────────────────────────────────────────────────
my_app/
│
├── gleam.toml      → What the project is + its deps
├── manifest.toml   → Exact locked versions (commit this)
├── README.md       → Human description
│
├── src/            → All application code (commit this)
│   └── *.gleam
│
├── test/           → All test code (commit this)
│   └── *_test.gleam
│
└── build/          → Compiled output (DO NOT commit)

Knowing where things live removes all guesswork from your daily workflow. You always know where to put new code and where to look for existing code — in any Gleam project, anywhere in the world.

Leave a Comment

Your email address will not be published. Required fields are marked *