Skip to content

Getting Started

Five concrete scenarios, each ~5 minutes. Pick the one that matches your immediate need.


Produce an invoice PDF with a heading, a paragraph, and a simple table.

Terminal window
dotnet add package Pragmatic.Documents.Model
dotnet add package Pragmatic.Documents.Pdf
using Pragmatic.Documents.Model;
using Pragmatic.Documents.Pdf;
var doc = new DocumentBuilder()
.Title("Invoice 2026-001")
.Author("Acme Corp")
.Size(PageSize.A4)
.WithMargins(Margins.Normal)
.Page(p => p
.Heading("Invoice 2026-001", level: 1)
.Paragraph(new TextNode { Content = "Thank you for your business." })
.Spacer(10)
.Table(t => t
.HeaderRow("Item", "Qty", "Price")
.Row("Design review", "1", "€250.00")
.Row("Implementation", "3", "€900.00"))
.Spacer(20)
.Paragraph(new TextNode { Content = "Total: €1150.00", Style = new NodeStyle { FontWeight = FontWeight.Bold } }))
.Build();
using var fs = File.Create("invoice.pdf");
PdfRenderer.RenderTo(fs, doc);
  • DocumentBuilder constructs an immutable DocumentModel with document metadata and a single page
  • PdfRenderer.RenderTo writes the PDF directly into the stream — no intermediate byte array

Open invoice.pdf in any viewer. See pdf-rendering.md for fonts, images, TOCs, and page numbering.


The document model is renderer-agnostic. Build once, render to both.

Terminal window
dotnet add package Pragmatic.Documents.Docx
using Pragmatic.Documents.Model;
using Pragmatic.Documents.Pdf;
using Pragmatic.Documents.Docx;
DocumentModel doc = BuildInvoice(); // same builder as before
using (var pdf = File.Create("invoice.pdf"))
PdfRenderer.RenderTo(pdf, doc);
using (var docx = File.Create("invoice.docx"))
new DocxRenderer().RenderTo(docx, doc);

Features like Toc(...) and page numbering Fields render correctly in both formats (viewers that auto-update fields refresh them on open). See docx-rendering.md for DOCX-specific options.


Export a list of guests to a styled XLSX.

Terminal window
dotnet add package Pragmatic.Documents.Spreadsheet
dotnet add package Pragmatic.Documents.Xlsx
using Pragmatic.Documents.Spreadsheet;
using Pragmatic.Documents.Xlsx;
var book = new SpreadsheetBuilder()
.Title("Guest Export")
.Author("Front Desk")
.Sheet("Guests", s => s
.Column(width: 8) // ID column
.Column(width: 20) // First name
.Column(width: 20) // Last name
.Column(width: 28) // Email
.Freeze(rows: 1, columns: 0) // header row always visible
.HeaderRow("Id", "First", "Last", "Email")
.Row(1, "Ada", "Lovelace", "ada@example.com")
.Row(2, "Grace", "Hopper", "grace@example.com")
.FormulaRow("=COUNTA(A2:A3)", "", "", "")) // count of rows
.Build();
using var fs = File.Create("guests.xlsx");
XlsxRenderer.RenderTo(fs, book);

The freeze pane and formula work when opened in Excel or LibreOffice. See xlsx-rendering.md for multi-sheet, merged cells, and per-cell styling.


Read an existing CSV, add a column, write it back.

Terminal window
dotnet add package Pragmatic.Documents.Csv
using Pragmatic.Documents.Csv;
using Pragmatic.Documents.Spreadsheet;
using var input = File.OpenRead("guests.csv");
var book = CsvReader.ReadAsModel(input, sheetName: "Guests");
// Add a "FullName" column
var sheet = book.Sheets[0];
sheet.Rows[0].Cells.Add(new Cell { Value = "FullName" });
for (int i = 1; i < sheet.Rows.Count; i++)
{
var first = sheet.Rows[i].Cells[1].Value;
var last = sheet.Rows[i].Cells[2].Value;
sheet.Rows[i].Cells.Add(new Cell { Value = $"{first} {last}" });
}
using var output = File.Create("guests-enriched.csv");
CsvWriter.Write(output, book);

CSV handles quoting, CRLF, embedded commas, and RFC 4180 edge cases out of the box. For locale-aware variants (; separator, , decimal) and formula-injection hardening see csv-io.md.


Build a verification email with a hero section and a call-to-action.

Terminal window
dotnet add package Pragmatic.Email.Model
dotnet add package Pragmatic.Documents.Email
using Pragmatic.Email.Model;
using Pragmatic.Documents.Email;
var email = new EmailBuilder()
.Subject("Verify your email address")
.Preheader("One click to finish signing up.")
.Language("en")
.Width(600)
.BackgroundColor("#f4f5f7")
.Section(s => s
.Heading("Welcome, Alice!")
.Paragraph("Thanks for signing up for Acme. We need to confirm your email before you can get started.")
.Button("Verify your email", "https://app.acme.com/verify?t=abc123")
.Paragraph("If you didn't create an account, you can safely ignore this email."))
.Build();
string html = new EmailHtmlRenderer().Render(email);
File.WriteAllText("verify.html", html);
// Hand the HTML to Pragmatic.Email or your SMTP library

The renderer emits email-client-safe HTML: inline CSS, MSO-compatible tables, preheader text, proper DOCTYPE. See email-rendering.md for advanced layouts (two-column, hero image, footer).


When designers or non-core developers own layout, use the markup DSL:

Terminal window
dotnet add package Pragmatic.Documents.Markup
dotnet add package Pragmatic.Documents.Templating

Write invoice.pdxdoc:

@title Invoice {{number}}
@author Pragmatic
# Invoice {{number}}
Thank you for your business, {{customer.name}}.
| Item | Qty | Price |
|------|-----|-------|
{{for item in items}}
| {{item.name}} | {{item.qty}} | {{item.price | currency:'EUR'}} |
{{endfor}}
**Total: {{total | currency:'EUR'}}**

Load, bind, render:

using Pragmatic.Documents.Markup;
using Pragmatic.Documents.Templating;
using Pragmatic.Documents.Pdf;
var template = PdxDocParser.ParseFile("invoice.pdxdoc");
var model = template.Resolve(new { number = "2026-001", customer = new { name = "Alice" }, items = ..., total = 1150m });
using var fs = File.Create("invoice.pdf");
PdfRenderer.RenderTo(fs, model);

See markup-parser.md for syntax and templating.md for expressions and pipes.

For AOT apps where the generator emits CSV serialisation at compile time:

Terminal window
dotnet add package Pragmatic.Documents.Csv
dotnet add package Pragmatic.Documents.Csv.Generator
[CsvSerializable<Guest>]
public partial class GuestCsv { }
public record Guest(int Id, string First, string Last);
// Zero reflection — generated at compile time
GuestCsv.Write(stream, guests);
var decoded = GuestCsv.Read(stream);

Every renderer ships with a runnable samples/ project you can dotnet run: