# johnfercher/maroto: Building PDFs in Go with a Bootstrap-Style Grid

> Maroto v2 wraps gofpdf in a row and column layout so Go services can assemble paginated PDFs without hand-placing every element. It suits server-side report generation, not pixel-perfect design work.

**johnfercher/maroto** — A maroto way to create PDFs. Maroto is inspired in Bootstrap and uses gofpdf. Fast and simple.

- Repository: https://github.com/johnfercher/maroto
- Website: https://maroto.tech
- Stars: 2,766 · Forks: 257
- Language: Go
- License: MIT
- Published: 2026-09-28 · Updated: 2026-09-28 · Language: en
- Canonical page: https://hysenlabs.com/projects/johnfercher-maroto

## The problem maroto solves for Go services

Generating a PDF in Go usually means choosing between two unsatisfying paths. You can drop to a low-level drawing API and compute every coordinate yourself, which turns a layout change into arithmetic. Or you can shell out to a headless browser, which adds a runtime dependency and a process boundary to something that should be a function call.

Maroto takes a third path. It is a Go library that sits on top of gofpdf and exposes a layout model borrowed from Bootstrap: a Row contains Cols, and a Col contains components. The README describes the intent directly: "You can write your PDFs like you are creating a site using Bootstrap. A Row may have many Cols, and a Col may have many components." The audience is Go developers producing documents from application code, where the content is data-driven and the layout repeats. Invoices, billing statements, barcode grids and reports are the shapes the repository's own examples cover.

The value is not that maroto can draw a rectangle. gofpdf already can. The value is that the grid, the page break and the header repeat are handled by the library rather than by your own bookkeeping.

## How the row, column and page model actually works

The mechanism is a layout tree over gofpdf. You declare structure, and the library resolves it into drawing calls. Two pieces of that tree are worth understanding before you write anything.

The first is automatic pagination. The README states that "pages will be added when content may extrapolate the useful area." You do not call an add-page function when a table grows; you keep adding rows and the library decides. The cmd and docs/assets/examples directories in the repository include an addpage example, which suggests the manual path exists too, but the default behaviour is content-driven.

The second is the repeating header. The README notes that you "can define a header which will be added always when a new page appear," and that a header may itself contain rows, lines or a tablelist. This is the feature that makes the library useful for multi-page documents, because a header that only appears on page one is a common source of post-processing fixes.

The dependency list in go.mod tells you what is underneath. The module is github.com/johnfercher/maroto/v2, and it requires github.com/phpdave11/gofpdf v1.4.3, github.com/boombuler/barcode v1.1.0, github.com/johnfercher/go-tree v1.1.0 and github.com/pdfcpu/pdfcpu v0.11.1. The barcode and pdfcpu entries line up with the barcodegrid and compression examples in the Makefile, so the library's scope reaches past plain text layout.

## Installing maroto v2 and rendering a first document

The README gives one installation command, pinned to the current release. Run it inside your module; it adds the dependency to go.mod and downloads the source.

```bash
go get github.com/johnfercher/maroto/v2@v2.4.2
```

The repository ships a runnable example for nearly every feature, and the Makefile exposes them through one target. Before writing your own code, running the examples is the fastest way to see what the grid produces.

```bash
make examples
```

That target runs Go files under docs/assets/examples, one directory per topic: addpage, autorow, background, barcodegrid, billing, cellstyle, checkbox, compression, customdimensions, customfont and custompage, among others. Each writes a PDF you can open. The billing example is the closest thing to a full document, and the barcodegrid example shows the barcode dependency in use.

If you want to work against the project's own toolchain, the Makefile defines the contributor path. The install target runs shell/install.sh and then points git at the tracked hooks directory.

```bash
make install
```

That sets core.hooksPath to .githooks and makes .githooks/pre-commit executable, so make dod runs before each commit and blocks it if build, test, fmt or lint fail. You do not need this to consume the library, only to modify it.

## Where maroto stops being the right tool

Maroto is a layout library, not a design tool. If your PDF has to match a designer's file to the millimetre, with elements placed at absolute coordinates and overlapping artwork, the row and column abstraction works against you. You will spend more effort fighting the grid than you would have spent placing elements directly through gofpdf.

The second boundary is language. Every API in this repository is Go. The module path, the examples, the Makefile targets and the CI configuration all assume a Go toolchain. If your service is written in Python, Java or TypeScript, maroto offers nothing you can call, and reaching it over a sidecar process would be a heavier solution than picking a native library.

The third is the dependency chain. Maroto does not implement PDF generation itself; it delegates to gofpdf. That means gofpdf's behaviour, its font handling and its limitations are part of your surface area, and the go.mod file shows a set of indirect dependencies pulled in through it. If your organisation restricts transitive dependencies, read go.sum before you commit.

One more gap: the README does not document rollback or version-migration guidance between minor releases. The repository keeps v1 alive on a separate branch, which tells you the v1 to v2 move was a real break, but nothing published describes a supported downgrade path.

## Maroto compared with wkhtmltopdf and headless Chrome

The obvious alternative for server-side PDF generation is HTML rendering: write a template, hand it to wkhtmltopdf or a headless browser, and get a PDF back. The difference in approach is where the layout decision lives.

With HTML rendering, your layout is CSS. You get the entire web layout model, plus text flow, web fonts and the ability to reuse a template that already exists as a web page. You also get a browser engine in your deployment, a rendering step that can take seconds, and a class of failures where the PDF looks different from the preview because the renderer's engine is not the one you tested in.

With maroto, your layout is Go code. There is no template language and no separate rendering process; the PDF is produced inside your binary. The cost is that you express layout in a smaller vocabulary: rows, columns and the components the library provides. There is no CSS cascade and no float behaviour to fall back on.

A closer comparison is gofpdf alone, since maroto is a layer over it. If you already know gofpdf and your documents have a fixed structure, the abstraction may not pay for itself. Maroto earns its place when the document grows, paginates and repeats headers, because those are the parts you would otherwise write and maintain by hand.

## Maintenance, licence and upgrade cost

The repository is not archived. The most recent push recorded for it is 2026-09-10, and the v2.4.2 release carries the same date. That is a recent change, and the release history shows v2.4.0 in March 2026, v2.4.1 in August 2026 and v2.4.2 in September 2026, so the project is moving in small increments rather than sitting still.

The maintenance burden on your side is low in one respect and non-zero in another. The library is a compile-time dependency, so there is no server to patch. But the go.mod file pins gofpdf, pdfcpu, barcode and go-tree, and those move independently. An upgrade of maroto can pull a new pdfcpu or a new gofpdf, and both touch PDF output. Treat a version bump as a change that can alter rendered bytes, not as a no-op.

The licence is MIT, stated in the LICENSE file at the repository root. MIT is permissive: it allows use, modification and redistribution, including in closed-source products, provided the copyright notice and permission notice are retained. That is a description of the licence text, not legal advice; if your organisation has a policy on attribution in distributed binaries, confirm how the notice is satisfied in your build.

The v1 branch still exists, and the README points to it as deprecated. If you are starting today, the v2 module path is github.com/johnfercher/maroto/v2, and importing the unversioned path is a mistake you will only notice when the API does not match the documentation.

## Conclusion

Adopt maroto if you generate structured documents from Go: invoices, reports, barcode labels, anything that follows a repeating grid and benefits from automatic page breaks and a repeated header. Do not adopt it if you need absolute control over element positions or you are not writing Go, because the whole API lives in that language. Before committing, verify two things yourself: render one of your real documents through the docs/assets/examples patterns and check the pagination behaviour when a row crosses a page boundary, and confirm that the gofpdf dependency in go.mod satisfies your own dependency policy.

## FAQ

### What is maroto v2 and how does it differ from v1?

Maroto v2 is the current line of the library, installed as github.com/johnfercher/maroto/v2, and the README presents v2.4.2 as the release to use. The v1 code still exists on a separate branch, which the README labels as deprecated.

### How do I install johnfercher/maroto in a Go project?

The README gives a single command, go get github.com/johnfercher/maroto/v2@v2.4.2, run inside your module. That adds the dependency to go.mod and downloads the source.

### Does maroto add new pages automatically?

Yes. The README states that pages will be added when content may extrapolate the useful area, so you keep adding rows and the library decides when to break. The repository also contains an addpage example under docs/assets/examples.

### What can I put in a maroto page header?

According to the README, a header is added whenever a new page appears, and it may contain many rows, lines or a tablelist. That makes it suitable for repeated table headings on multi-page documents.

## Sources

- [johnfercher/maroto on GitHub](https://github.com/johnfercher/maroto)
- [License: MIT](https://github.com/johnfercher/maroto/blob/master/LICENSE)
- [Project website](https://maroto.tech)
- [README](https://github.com/johnfercher/maroto/blob/master/README.md)
- [Releases](https://github.com/johnfercher/maroto/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/johnfercher-maroto
