> ## Documentation Index
> Fetch the complete documentation index at: https://docs.tipar.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# Images

> Embed a logo or graphic as base64, and control how it fits its space.

The `image` node embeds a raster image, supplied as **base64-encoded bytes** inside the request.

```json theme={"dark"}
{ "type": "image", "base64": "iVBORw0KGgoAAAANSUhEUgAA...", "fit": "fitWidth" }
```

| Field    | Type                       | Required | Notes                                                       |
| -------- | -------------------------- | -------- | ----------------------------------------------------------- |
| `base64` | string                     | **yes**  | The raw image bytes, base64-encoded. **No `data:` prefix.** |
| `fit`    | `"fitArea"` · `"fitWidth"` | no       | How the image scales into its space. Default `fitArea`.     |

Tipar passes the decoded bytes to the renderer, which detects the format — common raster formats such as **PNG** and **JPEG** work. URL-sourced images are not supported in v1; the bytes must travel in the request.

<Warning>
  Do **not** include the `data:image/png;base64,` prefix that browsers and some libraries prepend. Send only the base64 payload itself. A value the renderer can't decode (bad base64, or an unsupported format) returns [`422` `template.invalid_content`](/api-reference/errors#422-unprocessable-entity).
</Warning>

## Producing the base64

Encode the file's bytes — strip any data-URL prefix first.

<CodeGroup>
  ```javascript Node.js theme={"dark"}
  import { readFile } from "node:fs/promises";

  const base64 = (await readFile("logo.png")).toString("base64");
  // → drop into template: { type: "image", base64, fit: "fitWidth" }
  ```

  ```python Python theme={"dark"}
  import base64

  with open("logo.png", "rb") as f:
      b64 = base64.b64encode(f.read()).decode()
  ```

  ```bash curl theme={"dark"}
  base64 -i logo.png            # macOS
  base64 -w0 logo.png           # Linux (no line wrapping)
  ```
</CodeGroup>

## Controlling size with fit

`fit` decides how the image scales into the space its parent gives it. The space comes from the layout — most often a [`row` cell](/templates/layout#row) with a `constant` width, or the page width inside a `column`.

| `fit`                 | Behaviour                                                                            |
| --------------------- | ------------------------------------------------------------------------------------ |
| `fitArea` *(default)* | Scale to fit within both the available width and height, preserving aspect ratio.    |
| `fitWidth`            | Scale so the image is exactly as wide as its space; height follows the aspect ratio. |

<Note>
  **Size an image by its width, not its height.** There is no `fitHeight`. Content flows down the page, so the space a node is offered is bounded across but open-ended down — "make it exactly this tall" has nothing to resolve against. Put the image in a `constant`-width [row item](/templates/layout#row) or table cell and use `fitWidth`; the height then follows the aspect ratio. To keep a lane of logos visually even, crop or pad them to a common aspect ratio before encoding.
</Note>

A logo in a header lane is typically a fixed-width cell with `fitWidth`:

```json theme={"dark"}
{
  "type": "row",
  "items": [
    { "size": { "kind": "constant", "width": 120 }, "child": { "type": "image", "base64": "{{logo}}", "fit": "fitWidth" } },
    { "size": { "kind": "relative", "weight": 1 }, "child": { "type": "text", "value": "{{company.name}}", "style": { "fontSize": 18, "bold": true }, "align": "right" } }
  ]
}
```

<Note>
  `base64` is a `text`-like string, so it can be interpolated — `"base64": "{{logo}}"` pulls the encoded image from `data`. That keeps a large, static logo out of the template file and lets you swap it per tenant.
</Note>

<Tip>
  Images count toward the **4 MB request body cap**. Optimise and resize before encoding — a 200 KB logo is plenty at print resolution, and base64 adds \~33% on top. Don't ship a 5 MB photo where a downscaled one will do.
</Tip>

<Card title="Next: Headers & footers" icon="window-maximize" href="/templates/headers-footers">
  Repeat content on every page and add page numbers.
</Card>
