# gssg
[](https://hex.pm/packages/gssg)
[](https://hexdocs.pm/gssg/)
Batteries-not-included, templating library agnostic SSG library for Gleam.
## Features
- Simple API: Just four functions to build a complete static site
- Type-safe: Leverages Gleam's type system for compile-time guarantees
- Flexible: Works with any page type (Lustre elements, strings, custom types)
- Dynamic routes: Generate multiple pages from data (perfect for blog posts)
- Zero config: No configuration files needed, just code
## Installation
```sh
gleam add gssg
```
## Quick Start
```gleam
import gleam/dict
import gleam/io
import gleam/string
import lustre/element
import gssg/ssg
pub fn main() -> Nil {
let result =
ssg.new("priv", element.to_document_string)
|> ssg.add_static_route("/", home_view())
|> ssg.add_static_route("/about", about_view())
|> ssg.build()
case result {
Ok(_) -> io.println("Build successful!")
Error(e) -> io.println("Build failed: " <> string.inspect(e))
}
}
fn home_view() {
element.html([], [
element.head([], [element.title([], "Home")]),
element.body([], [element.h1([], [element.text("Welcome!")])]),
])
}
fn about_view() {
element.html([], [
element.head([], [element.title([], "About")]),
element.body([], [element.h1([], [element.text("About Us")])]),
])
}
```
This generates:
- `priv/index.html` - Your home page
- `priv/about/index.html` - Your about page
## Usage
### Creating a site
Start by creating an SSG instance with an output directory and a render function:
```gleam
import lustre/element
import gssg/ssg
let site = ssg.new("output", element.to_document_string)
```
The render function converts your page type to HTML strings. For Lustre elements, use `element.to_document_string`.
### Adding static routes
Add individual pages with `add_static_route`:
```gleam
ssg.new("priv", element.to_document_string)
|> ssg.add_static_route("/", home.view())
|> ssg.add_static_route("/projects", projects.view())
|> ssg.add_static_route("/contact", contact.view())
```
Each route generates a file at `{path}/index.html`.
### Adding dynamic routes
Generate multiple pages from a collection of data:
```gleam
import gleam/dict
import markup/markup
pub fn main() -> Nil {
// Parse blog posts from markdown files
let assert Ok(posts) = markup.parse_dir("data/posts")
ssg.new("priv", element.to_document_string)
|> ssg.add_static_route("/", home.view())
|> ssg.add_static_route("/blog", blog_index.view(posts))
|> ssg.add_dynamic_route("/blog", dict.from_list(posts), blog_post.view)
|> ssg.build()
}
```
If `posts` contains:
- `#("hello-world", post1)`
- `#("getting-started", post2)`
This generates:
- `priv/blog/hello-world/index.html`
- `priv/blog/getting-started/index.html`
### Building the site
Call `build()` to render and write all files:
```gleam
let result = ssg.build(site)
case result {
Ok(_) -> io.println("Build successful!")
Error(ssg.CannotWriteFile(path, reason)) -> {
io.println("Failed to write: " <> path)
}
}
```
## Complete Example
Here's a complete example with static pages and a blog:
```gleam
import gleam/dict
import gleam/io
import gleam/string
import lustre/element
import markup/markup
import pages/home
import pages/writing/post
import pages/writing/writing
import gssg/ssg
pub fn main() -> Nil {
let assert Ok(posts) = markup.parse_dir("data/posts")
let result =
ssg.new("priv", element.to_document_string)
|> ssg.add_static_route("/", home.view())
|> ssg.add_static_route("/projects", markup.from("data/pages/projects.dj"))
|> ssg.add_static_route("/writing", writing.view(posts))
|> ssg.add_dynamic_route("/writing", dict.from_list(posts), post.view)
|> ssg.build()
case result {
Ok(_) -> io.println("build successful.")
Error(e) -> io.println("build failed: " <> string.inspect(e))
}
}
```
## License
This project is licensed under the Apache-2.0 License.