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

# Rendering

> Output formats and rendering options

# Rendering

Pictify renders HTML/CSS templates to various output formats. This page covers rendering options, formats, and optimization.

## Output Formats

### Images

| Format | Extension | Best For                                | Transparency |
| ------ | --------- | --------------------------------------- | ------------ |
| PNG    | `.png`    | Screenshots, graphics with transparency | Yes          |
| JPEG   | `.jpg`    | Photos, smaller file sizes              | No           |
| WebP   | `.webp`   | Modern browsers, best compression       | Yes          |

### Documents

| Format | Extension | Best For                             |
| ------ | --------- | ------------------------------------ |
| PDF    | `.pdf`    | Print, documents, multi-page content |

### Animated

| Format | Extension | Best For                |
| ------ | --------- | ----------------------- |
| GIF    | `.gif`    | Animations, short loops |

## Rendering Options

### Dimensions

```typescript theme={null}
const image = await pictify.renderHtml({
  html: '<h1>Hello</h1>',
  width: 1200,    // Output width in pixels (default: 1280)
  height: 630,    // Output height in pixels (default: 720)
});
```

<Note>
  Maximum dimensions: 4000x4000 pixels for authenticated users, 2000x2000 for public/trial.
</Note>

### Format

`renderHtml` writes a PNG by default. Set `format` to choose the output type — it maps to the `/image` endpoint's `fileExtension`:

```typescript theme={null}
const image = await pictify.renderHtml({
  html: '<h1>Hello</h1>',
  format: 'jpeg'   // 'png' | 'jpg' | 'jpeg' | 'webp' | 'pdf' (default: png)
});
```

<Note>
  The `/image` endpoint does not expose a `quality` knob. To control raster compression, render through a [template](/concepts/templates) (`pictify.render({ templateId, quality })`, where `quality` is `0.1`–`1.0`).
</Note>

### Selector

Capture a specific element instead of the full page:

```typescript theme={null}
const image = await pictify.renderHtml({
  html: '<div><header>...</header><main id="content">Target</main></div>',
  selector: '#content'  // Only capture this element
});
```

## Image Generation

### From HTML

```typescript theme={null}
const image = await pictify.renderHtml({
  html: `
    <div style="width: 1200px; height: 630px; background: #667eea;">
      <h1 style="color: white;">Hello World</h1>
    </div>
  `,
  width: 1200,
  height: 630
});

console.log(image.url);
```

### From URL

Capture a screenshot of any URL:

```typescript theme={null}
const screenshot = await pictify.renderUrl({
  url: 'https://example.com/page',
  width: 1200,
  height: 630
});

console.log(screenshot.url);
```

### From Template

Render a saved template with variables:

```typescript theme={null}
const result = await pictify.render({
  templateId: 'template-id',
  variables: {
    title: 'Dynamic Content'
  }
});

// Access the rendered image (result.url is a convenience accessor for results[0].url)
console.log(result.url);
console.log(result.results[0].url);
```

### Layout Variants

Templates can have multiple layout variants for different platforms (e.g., Twitter, Facebook, Instagram). Render a specific layout or multiple layouts at once:

```typescript theme={null}
// Render a specific layout
const single = await pictify.render({
  templateId: 'template-id',
  variables: { title: 'Hello' },
  layout: 'twitter-post'
});

// Render multiple layouts in one request (max 20)
const result = await pictify.renderLayouts({
  templateId: 'template-id',
  variables: { title: 'Hello' },
  layouts: ['default', 'twitter-post', 'facebook-post']
});

// Each layout is a separate result
result.results.forEach(r => {
  console.log(`${r.name} (${r.width}x${r.height}): ${r.url}`);
});
```

<Note>
  The `default` layout is the base template. Use the layout key (e.g., `twitter-post`, `facebook-post`) to render a specific variant. Layout keys are set when creating variants via the AI Resize feature in the editor.
</Note>

### From Canvas Data

Render FabricJS canvas data directly via the `POST /image/canvas` REST endpoint. The SDKs don't wrap this endpoint, so call it with your HTTP client of choice:

```bash theme={null}
curl -X POST https://api.pictify.io/image/canvas \
  -H "Authorization: Bearer $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "engine": "fabric",
    "fabricJSData": {
      "version": "5.3.0",
      "objects": [
        { "type": "rect", "fill": "#667eea", "width": 1200, "height": 630 },
        { "type": "textbox", "text": "Hello", "fill": "white", "top": 100, "left": 100 }
      ]
    },
    "variables": { "greeting": "Hello World" },
    "width": 1200,
    "height": 630
  }'
```

See the [Create Canvas Image](/api-reference/endpoints/images/canvas) reference for the full request schema.

## GIF Generation

### From HTML Animation

Capture CSS animations:

```typescript theme={null}
const gif = await pictify.renderGif({
  html: `
    <div class="animated">
      <style>
        @keyframes fade { 0% { opacity: 0; } 100% { opacity: 1; } }
        .animated { animation: fade 2s infinite; }
      </style>
      <h1>Animated Text</h1>
    </div>
  `,
  width: 600,
  height: 400
});

console.log(gif.url, gif.animationLength);
```

### From a Live URL

Record motion on a live page:

```typescript theme={null}
const gif = await pictify.renderGif({
  url: 'https://example.com/animated-page',
  width: 800,
  height: 600,
  quality: 'high'   // 'low' | 'medium' | 'high' (default: medium)
});
```

### Quality Presets

| Preset   | Frame Rate | Max Duration | File Size |
| -------- | ---------- | ------------ | --------- |
| `low`    | 10 fps     | 10s          | Small     |
| `medium` | 15 fps     | 15s          | Medium    |
| `high`   | 24 fps     | 30s          | Large     |

## PDF Generation

### From a Template (SDK)

The quickest way to produce a PDF is to render a template with `format: 'pdf'`:

```typescript theme={null}
const result = await pictify.render({
  templateId: 'invoice-template',
  variables: {
    invoiceNumber: 'INV-001',
  },
  format: 'pdf'
});

console.log(result.url);
```

### Paper Sizes & Multi-Page (REST)

The dedicated PDF endpoints (`POST /pdf/render` and `POST /pdf/multi-page`) accept paper-size presets, margins, and per-page content. The SDKs don't wrap these directly, so call them with your HTTP client:

```bash theme={null}
# Single page with a paper-size preset and margins
curl -X POST https://api.pictify.io/pdf/render \
  -H "Authorization: Bearer $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "templateUid": "invoice-template",
    "variables": { "invoiceNumber": "INV-001" },
    "preset": "A4",
    "margins": { "top": 20, "bottom": 20, "left": 20, "right": 20 }
  }'
```

```bash theme={null}
# Multi-page: one page per variable set
curl -X POST https://api.pictify.io/pdf/multi-page \
  -H "Authorization: Bearer $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "templateUid": "report-template",
    "variableSets": [
      { "pageTitle": "Introduction", "content": "..." },
      { "pageTitle": "Chapter 1", "content": "..." },
      { "pageTitle": "Conclusion", "content": "..." }
    ],
    "preset": "A4"
  }'
```

See the [PDF Generation](/api-reference/generation/pdfs) reference for all options.

### PDF Presets

| Preset   | Dimensions   | Use Case           |
| -------- | ------------ | ------------------ |
| `A4`     | 210 × 297 mm | Standard documents |
| `Letter` | 8.5 × 11 in  | US letter size     |
| `Legal`  | 8.5 × 14 in  | Legal documents    |
| `A3`     | 297 × 420 mm | Larger documents   |
| `A5`     | 148 × 210 mm | Booklets           |
| `custom` | User-defined | Custom sizes       |

```bash theme={null}
# Custom PDF size
curl -X POST https://api.pictify.io/pdf/render \
  -H "Authorization: Bearer $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "templateUid": "template-id",
    "preset": "custom",
    "customSize": { "width": 800, "height": 600 }
  }'
```

## Screenshot a Section of a Page

To capture a specific region of a live page, screenshot the URL and crop to an element with `selector`:

```typescript theme={null}
const screenshot = await pictify.renderUrl({
  url: 'https://stripe.com/pricing',
  selector: '#pricing',   // crop to this element
  width: 1200,
  height: 630
});

console.log(screenshot.url);
```

<Note>
  Prefer natural language? The [Pictify MCP server](/agent-integration/mcp-server) lets AI agents request screenshots conversationally — the SDKs themselves expose the direct `renderUrl` call shown above.
</Note>

## Response Format

The template render endpoint always returns a `results` array, even for single-layout renders:

```json theme={null}
{
  "results": [
    {
      "layout": "default",
      "name": "Default",
      "url": "https://cdn.pictify.io/renders/abc123.png",
      "width": 1200,
      "height": 630,
      "format": "png",
      "id": "abc123"
    }
  ],
  "errors": [],
  "totalLayouts": 1,
  "totalRendered": 1,
  "totalErrors": 0,
  "templateUid": "TEMPLATE_UID"
}
```

Multi-layout response:

```json theme={null}
{
  "results": [
    {
      "layout": "default",
      "name": "Default",
      "url": "https://cdn.pictify.io/renders/abc123.png",
      "width": 1080,
      "height": 1080,
      "format": "png"
    },
    {
      "layout": "twitter-post",
      "name": "Twitter/X Post",
      "url": "https://cdn.pictify.io/renders/def456.png",
      "width": 1200,
      "height": 675,
      "format": "png"
    }
  ],
  "errors": [],
  "totalLayouts": 2,
  "totalRendered": 2,
  "totalErrors": 0,
  "templateUid": "TEMPLATE_UID"
}
```

Other render endpoints (HTML, URL, GIF) return:

```json theme={null}
{
  "url": "https://cdn.pictify.io/renders/abc123.png",
  "id": "abc123",
  "width": 1200,
  "height": 630,
  "createdAt": "2026-01-29T10:30:00Z"
}
```

## User Storage

If you've configured user storage (S3, R2, etc.), renders are also uploaded to your storage:

```json theme={null}
{
  "url": "https://cdn.pictify.io/renders/abc123.png",
  "userStorageUrl": "https://your-bucket.s3.amazonaws.com/renders/abc123.png"
}
```

## Batch Rendering

Render many variable sets from a single template. Batch rendering is **asynchronous**: submitting returns immediately with a `batchId`, and the job runs in the background.

### Request

```typescript theme={null}
const job = await pictify.renderBatch({
  templateId: 'product-card',
  variableSets: [
    { title: 'Card 1' },
    { title: 'Card 2' },
  ], // max 100 per batch
  layouts: ['default', 'twitter-post'], // optional; or `layout: 'square'`
});

// { batchId, status, totalItems }
console.log(job.batchId);
```

Pass `layout` (string) for a single variant, or `layouts` (array) for multiple variants per item.

### Tracking Progress

Poll `getBatchResults(batchId)` to track status. The poll response reports per-item `index`, `success`, and `variables` — but **not** rendered URLs:

```json theme={null}
{
  "batchId": "batch_abc123",
  "status": "completed",
  "progress": 100,
  "totalItems": 2,
  "completedItems": 2,
  "failedItems": 0,
  "results": [
    { "index": 0, "success": true, "variables": ["title"] },
    { "index": 1, "success": true, "variables": ["title"] }
  ],
  "errors": []
}
```

```typescript theme={null}
const status = await pictify.getBatchResults(job.batchId);
console.log(status.status, status.completedItems, 'of', status.totalItems);
for (const item of status.results) {
  console.log(`item ${item.index}: success=${item.success}`);
}
```

<Warning>
  **Rendered URLs are not returned by the poll endpoint.** Final image URLs are delivered via the `render.completed` webhook — subscribe to [webhooks](/concepts/webhooks) to collect batch output. See the [Batch Processing guide](/guides/batch-processing) for the full workflow.
</Warning>

## Performance Tips

1. **Use appropriate dimensions** - Don't render larger than needed
2. **Choose the right format** - JPEG for photos, PNG for graphics, WebP for best compression
3. **Tune template quality** - for template renders, `quality` 0.9 is usually indistinguishable from 1.0
4. **Batch similar renders** - Use batch rendering for many images from one template
5. **Cache rendered images** - Store URLs and reuse instead of re-rendering
