Getting started
Installation
Install Go and the collage CLI, scaffold a project with collage new, and run it under collage dev.
A collage project is an ordinary Go module. The collage command-line tool
scaffolds one, runs it while you work on it, and builds it when you ship it; the
framework itself is a library the project imports. Neither needs anything beyond
Go.
Install Go
collage needs Go 1.26 or newer. Install it from go.dev/dl or your package manager, and check:
go version
Install the CLI
go install github.com/Elagoht/collage/cmd/collage@latest
go install puts the binary in $(go env GOBIN), or in $(go env GOPATH)/bin
when GOBIN is not set; that directory must be on your PATH. Check that it is:
collage version
The version it prints is read from the build, so it is whatever go install
fetched. collage help lists every command, and collage help new describes one.
Create a project
collage new mysite
This writes a runnable project into ./mysite, with the module path mysite, and
prints what to do next:
Scaffolded "mysite" in mysite
Next steps:
cd mysite
go mod tidy
cp .env.example .env.development
collage dev
go mod tidy fetches the framework. The scaffolded go.mod names only the module
and the Go version, so the first tidy is what records which collage release the
project is built against.
A few flags change where and how it is written. They take one dash or two, and may come before or after the name:
| Flag | Effect |
|---|---|
--template minimal |
One layout around one page and a stylesheet — no demos. The default is --template demo |
-dir path |
Scaffold into path instead of ./<name> |
-module path |
The module path in go.mod, such as github.com/you/mysite. Defaults to the name |
-force |
Scaffold into a directory that is not empty |
collage new mysite -module github.com/you/mysite
collage new mysite --template minimal
collage new mysite -dir . -force
With the demos, or without
The default template, demo, gives you a home page and a /features page of live demos: a
button posting to an API action that invalidates a cached page by tag, an HTML
form posting to its own page, a clock fragment opened at its own URL, and a JSON
document at /healthz. It is the quickest way to see each of those working, and
the code behind each is short enough to read in one sitting.
--template minimal gives you the same main.go and directory layout, and in it
only a layout around one page saying hello, with a stylesheet that has a dark
mode — nothing to delete. Use it when you are starting a real site.
Your first page starts from a minimal project.
What the scaffold contains
A minimal project looks like this:
mysite/
├── main.go configuration, the static mount, the CLI contract, plugin commands
├── routes.go every page, document and action — a new route goes here
├── go.mod
├── .gitignore
├── README.md
├── pages/
│ └── home.go the home page: layout, content, path
├── fragments/
│ └── layouts/main.go the layout fragment every page shares, and the site's title
├── templates/
│ ├── layouts/default.html the layout's HTML, with {{slot "content"}}
│ └── pages/home.html <h1>Hello from collage</h1>
└── static/
└── app.css the background and text colour, light and dark
The demo project adds main_test.go, whose tests drive app.Handler() with no
server; a not-found page; .env.example, the variables for collage dev to copy;
plugins-config.json, plugin settings keyed by plugin name; a favicon; and
actions/, documents/, store/ and fragments/demo/, with their templates and
scripts.
A few things in it are worth knowing before you change them.
main.go keeps a contract with the CLI. collage dev runs the program with
COLLAGE_DEV=1 in its environment, and collage export runs it with
-collage-build -out <dir>. The scaffolded main.go reads both: the variable
turns on development mode, and the flag renders the site to files instead of
serving it. A word after the flags — go run . <command> — runs a
plugin's command. If you rewrite main.go, keep all of it
working, or those commands stop doing anything useful.
The CLI reference has the details.
newApp is separate from main. It builds the whole application — the
configuration, the routes from routes.go, the /static/ mount — and returns it
without starting a server. The demo project's main_test.go calls the same function and drives
app.Handler() with net/http/httptest, so the tests exercise the site that
actually runs rather than a second wiring of it. See Testing.
Templates and static files are embedded. //go:embed all:templates and
//go:embed all:static put them inside the binary, so it runs from any working
directory. In development the directories on disk win whenever they are there, so
an edited template still shows up on the next request.
In development, static files are mounted with os.OpenRoot, not os.DirFS.
An os.Root refuses a symlink that leads out of the directory; os.DirFS follows
it. In production the embedded copy is served, through fs.Sub so that its
static/ directory is not a second path segment. See Static assets.
Rendered pages are cached on disk in production, under .cache/. In
development the page cache is never read, so an edit is never hidden behind a
stale page. See Caching.
The environment file
The scaffold ships .env.example:
COLLAGE_CSRF_KEY=
PORT=3000
HOST=localhost
Copy it to .env.development, which is ignored by git:
cp .env.example .env.development
collage dev adds the variables in .env.development to the program's
environment — or those in .env, when there is no .env.development. It reads one
file, never both. The rules are short:
- A variable already set in your shell wins, so
PORT=4000 collage devworks.COLLAGE_DEV=1is always set, whatever the file says. - The file holds
KEY=valuelines,#comments and blank lines. Anexportprefix and quotes around a value are allowed. - A malformed line is reported with the file name and line number, rather than
skipped: a skipped line is a setting you wrote and the program never saw. Until
you fix it nothing is started or restarted — a build already running keeps
serving — and
collage devkeeps watching, so saving the fix carries on. - No file at all is not an error. When there is one, its name is printed on stderr.
Only collage dev reads these files. collage build, collage export and the
built binary never do; in production the environment comes from wherever the
binary runs.
COLLAGE_CSRF_KEY signs the tokens forms carry. Empty is fine while you develop —
a key is generated for each process. Set one before you deploy anything with a
form, or every form submitted before a restart is refused after it. A cached page
with a form in it is tied to the key too: after a restart with a new one, that page
is rendered afresh rather than served with the old key's token in it — see
Caching. Make a key with:
openssl rand -hex 32
Run it
collage dev
The site is at http://localhost:3000. Three things happen while it runs.
Go changes are rebuilt
collage dev builds the project with go build and runs the binary. It watches
what the program is made of — .go files (tests aside), go.mod, go.sum and the
environment file — and on a change it rebuilds.
The new build is made first. Only once it compiles is the old process stopped,
gracefully, and the new one started. A change that does not compile leaves the
last good build serving and prints the compiler's error in the terminal, so a
typo never leaves you with nothing at localhost:3000.
A burst of saves is one rebuild. Hidden directories, bin, dist,
node_modules, testdata and vendor are never watched, so nothing the running
program writes can set off a rebuild of itself. And a program that exits by itself
— a panic at startup, a port already in use — is not restarted in a loop; your
next change starts it again.
Templates and static files are read from disk
In development mode every template is reparsed from disk before each render, and
static files are served from static/ on disk. Editing either needs no rebuild,
and none happens.
The browser reloads itself
Every page served in development carries a small script that reloads it when a
template or a static file changes, and when the program comes back from a
rebuild. Save a file and look at the browser; there is nothing to install.
Content your program reads from disk itself — Markdown, JSON — is neither a
template nor a static file, so name its directory in
Config.DevWatch (since v0.10.0) to have its
changes reload the page too. The script is never added to a production page, nor
to the answer to a form submission, which reloading would submit again.
Errors show up on the page
A fragment that fails in development does not quietly vanish. The page is served with a panel over it naming the fragment and its error — for a template, with the file and line — even when a fallback covered for it. When the whole page fails, the built-in error page names the fragment where the failure started and shows the full error chain, including a panic's stack — and an error page of your own gets the same panel on top, saying what it is standing in for.
None of that exists outside development. A production error page says one generic sentence, because error messages carry hostnames, file paths and credentials, and an error page is exactly the response most likely to hand them to a stranger. See Errors.
Next
Your first page builds a page with data from nothing, in
a minimal project. When you are ready to ship, collage build makes a binary and
collage export writes static files — see Deployment and
Static export.