# Documents

Create and render static documents with layers

Canonical URL: https://docs.paradoc.dev/guides/documents/

This guide walks through creating a static document with multiple layers and rendering it.

## Define the Document

A document needs a name, version, and title. Unlike forms, documents don't have fields or parties—they're static content with one or more layers.



**Object Pattern**

```ts
import { p } from '@paradoc/sdk'

const privacyPolicy = p.document({
  name: 'privacy-policy',
  version: '1.0.0',
  title: 'Privacy Policy',
  description: 'Our privacy policy document',
  code: 'DOC-PRIVACY-001',
})
```

**Builder Pattern**

```ts
import { p } from '@paradoc/sdk'

const privacyPolicy = p
  .document()
  .name('privacy-policy')
  .version('1.0.0')
  .title('Privacy Policy')
  .description('Our privacy policy document')
  .code('DOC-PRIVACY-001')
  .build()
```

## Add Layers

Documents support multiple layers for different formats. Add inline layers for text, HTML, or Markdown content.



**Object Pattern**

```ts
const privacyPolicy = p.document({
  name: 'privacy-policy',
  version: '1.0.0',
  title: 'Privacy Policy',
  defaultLayer: 'markdown',
  layers: {
    markdown: {
      kind: 'inline',
      mimeType: 'text/markdown',
      text: `
# Privacy Policy

Last Updated: January 2025

## Data Collection

We collect information you provide directly to us, including name, email, and usage data.

## Data Usage

Your data is used to provide and improve our services.
      `,
    },
    html: {
      kind: 'inline',
      mimeType: 'text/html',
      text: '<h1>Privacy Policy</h1><p>Last Updated: January 2025</p>',
    },
  },
})
```

**Builder Pattern**

```ts
const privacyPolicy = p
  .document()
  .name('privacy-policy')
  .version('1.0.0')
  .title('Privacy Policy')
  .inlineLayer('markdown', {
    mimeType: 'text/markdown',
    text: `
# Privacy Policy

Last Updated: January 2025

## Data Collection

We collect information you provide directly to us, including name, email, and usage data.

## Data Usage

Your data is used to provide and improve our services.
    `,
  })
  .inlineLayer('html', {
    mimeType: 'text/html',
    text: '<h1>Privacy Policy</h1><p>Last Updated: January 2025</p>',
  })
  .defaultLayer('markdown')
  .build()
```

## Add File Layers

You can also reference external files instead of inline content. A file-backed
layer's bytes come from a resolver, and the resolver is bound once, when the
document is constructed — every instance derived from it afterward carries it.



**Object Pattern**

```ts
import { createFsResolver } from '@paradoc/resolvers/fs'

const resolver = createFsResolver({ root: process.cwd() })

const privacyPolicy = p.document({
  name: 'privacy-policy',
  version: '1.0.0',
  title: 'Privacy Policy',
  defaultLayer: 'pdf',
  layers: {
    pdf: {
      kind: 'file',
      mimeType: 'application/pdf',
      path: '/documents/privacy-policy.pdf',
    },
    markdown: {
      kind: 'file',
      mimeType: 'text/markdown',
      path: '/documents/privacy-policy.md',
    },
  },
}, { resolver })
```

**Builder Pattern**

```ts
import { createFsResolver } from '@paradoc/resolvers/fs'

const resolver = createFsResolver({ root: process.cwd() })

const privacyPolicy = p
  .document()
  .name('privacy-policy')
  .version('1.0.0')
  .title('Privacy Policy')
  .fileLayer('pdf', {
    mimeType: 'application/pdf',
    path: '/documents/privacy-policy.pdf',
  })
  .fileLayer('markdown', {
    mimeType: 'text/markdown',
    path: '/documents/privacy-policy.md',
  })
  .defaultLayer('pdf')
  .build({ resolver })
```

## Render the Document

Documents return raw content directly—no renderer needed. Use the default layer or specify one explicitly.

```ts
// Render using default layer
const output = await privacyPolicy.render()
console.log(output)

// Render specific layer
const htmlOutput = await privacyPolicy.render({ layer: 'html' })
console.log(htmlOutput)
```

A document with file layers, built with a bound resolver as shown above,
renders the same way — no resolver in the render call:

```ts
const pdfBytes = await privacyPolicy.render()
```

Render a file layer with no resolver bound and Paradoc throws
`UnboundResolverError`, naming the layer and the path it wanted.

## Save the Artifact

Export the document to JSON or YAML for storage or version control.

```ts
const json = privacyPolicy.toJSON()
const yaml = privacyPolicy.toYAML()
```



**JSON**

```json
{
  "$schema": "https://schema.paradoc.dev/2026-10-02.json",
  "kind": "document",
  "name": "privacy-policy",
  "version": "1.0.0",
  "title": "Privacy Policy",
  "description": "Our privacy policy document",
  "code": "DOC-PRIVACY-001",
  "layers": {
    "markdown": {
      "kind": "inline",
      "mimeType": "text/markdown",
      "text": "..."
    }
  },
  "defaultLayer": "markdown"
}
```

**YAML**

```yaml
$schema: https://schema.paradoc.dev/2026-10-02.json
kind: document
name: privacy-policy
version: 1.0.0
title: Privacy Policy
description: Our privacy policy document
code: DOC-PRIVACY-001
layers:
  markdown:
    kind: inline
    mimeType: text/markdown
    text: "..."
defaultLayer: markdown
```

Load it back with `p.load()`:

```ts
const loaded = p.load(jsonString) // works with JSON or YAML
```
