A practical guide to constructing a minimal, functional blog using the Gleam programming language, demonstrating how static site generation can be achieved with a small, maintainable codebase while maintaining flexibility for future expansion.
The modern web development landscape presents a paradox of choice: an overwhelming array of frameworks and tools, each promising to streamline the creation of a simple blog, yet often introducing layers of complexity that obscure the fundamental act of writing and publishing. This tension between simplicity and capability is precisely where Gleam, a statically typed functional language for the Erlang VM and JavaScript, offers a compelling alternative. The project detailed here—a blog generator written in Gleam—serves not merely as a technical tutorial but as a philosophical statement: that a blog, at its core, is a collection of text files that should be transformable into HTML with minimal ceremony, and that the tooling should be transparent, extensible, and written in a language that encourages clarity.
The foundation of this system rests on a few key dependencies. The lustre library provides a functional, declarative way to generate HTML elements, while simplifile and filepath handle the necessary file system operations. The core insight is that the Gleam code runs only at build time, producing a directory of static files—HTML, CSS, images—that can be served by any basic web host. This static site generation (SSG) approach decouples the runtime environment from the build process, offering performance benefits and security advantages, as there is no server-side execution when a user visits the site. The initial setup is straightforward: a new Gleam project with a blog.gleam entrypoint that defines an out_directory for the built site and a simple index() function returning a Lustre element. The main function orchestrates the build, ensuring the output directory exists and writing the rendered HTML document to index.html. This baseline demonstrates the core workflow: define a view, convert it to a string, and write it to disk.
The next layer introduces dynamic content: blog posts. The chosen format is Markdown, parsed by the mork library. The build script scans a designated blog_directory for .md files, reads their contents, parses the Markdown into HTML, and stores the result in a BlogPost type containing a slug (for the URL) and the HTML content. Each post is then written to its own HTML file, creating individual pages. However, a collection of isolated pages is not a blog; it requires a mechanism for discovery. This is achieved by introducing frontmatter—metadata at the top of each Markdown file, delimited by ---. Using the frontmatter and tom libraries, the script extracts a title, description, and date for each post. The BlogPost type is extended to include these fields, and the list of posts is sorted by date to present them chronologically on the homepage. The home page is dynamically generated from this list, creating links to each post with its metadata. This transforms the static generator into a data-driven system where content and presentation are separate concerns.
A functional blog is not complete without visual styling and media. The guide presents two paths: using a utility-first CSS framework like Tailwind, which integrates seamlessly with the build process, or using plain CSS. The latter is implemented by creating a static directory containing assets like style.css and images. The build script is updated to copy this entire directory to the output, ensuring all assets are deployed alongside the HTML. A page helper function is introduced to wrap all generated content in a consistent HTML document structure, including a link to the stylesheet. This demonstrates a key principle of SSG: the build process is responsible for assembling all components—content, layout, and assets—into a coherent whole before deployment.
The final feature discussed is an RSS feed, a crucial tool for readers who prefer feed readers over direct website visits. Using the webls library, the script constructs an RSS channel from the sorted list of blog posts, mapping each BlogPost to an RSS item with its title, description, and a canonical URL. The feed is written to feed.xml in the output directory. This addition highlights the extensibility of the Gleam-based system: adding a new output format (RSS) is a matter of adding a new library and a few lines of code that transform the existing data structure into a new representation.
The entire project, from setup to deployment, is remarkably concise. The author notes that the core logic amounts to just 182 lines of code across a handful of files. This is not an accident of brevity but a consequence of Gleam's design and the choice of a static site generation model. The language's functional nature encourages composing small, pure functions that transform data, which aligns perfectly with the task of converting Markdown files into HTML documents. The absence of a complex runtime framework eliminates the need for routing, state management, or server-side rendering, reducing the cognitive load on the developer.
The implications of this approach are significant for developers seeking a balance between control and convenience. Unlike monolithic frameworks that dictate architecture and often require learning a proprietary ecosystem, this Gleam blog is a transparent, malleable codebase. It can be a starting point for a personal blog or the foundation for a more complex content management system. The build script is the single source of truth; there is no hidden magic. For those familiar with Gleam's syntax and type system, extending the system—adding tags, categories, or a different content format—is a straightforward exercise in data transformation.
One might argue that using a functional language like Gleam for a simple blog generator is overkill, especially when simpler tools like Jekyll or Hugo exist. However, the value lies in the learning experience and the control it affords. Building a generator from scratch demystifies the process of static site generation. It reveals that under the hood, even sophisticated SSGs are essentially file processors. By writing it in Gleam, a developer gains proficiency in a modern functional language while producing a tool tailored to their exact needs. The trade-off is a steeper initial learning curve for those new to Gleam or functional programming, but the payoff is a deep understanding of the system and a highly customizable result.
In conclusion, this Gleam blog generator exemplifies a return to fundamentals. It strips away the non-essential, focusing on the core tasks of reading files, transforming content, and writing output. It demonstrates that with a thoughtfully chosen language and a clear architectural model, one can build a powerful, extensible tool with minimal code. The project is not just a blog; it is a statement on the value of simplicity, transparency, and the joy of building your own tools. For the developer weary of framework fatigue, it offers a refreshing path forward: a small, understandable system that does exactly what you need, and nothing more.
Relevant URLs:

Comments
Please log in or register to join the discussion