OpenForm

Forms

Last updated on

Create, fill, and render a simple lease agreement

This guide walks through creating a lease agreement, filling it with data, and rendering the output.

Create a new project

Create a new project, set up the directory structure, and download the W-9 form:

mkdir my-form && cd my-form
npm init -y
mkdir my-form && cd my-form
pnpm init -y
mkdir my-form && cd my-form
yarn init -y
mkdir my-form && cd my-form
bun init -y

Install the SDK

Install the SDK and the filesystem resolver.

npm install @open-form/sdk
pnpm add @open-form/sdk
yarn add @open-form/sdk
bun add @open-form/sdk

Define the Form

A form needs a name, fields, and parties. Here's a simple lease agreement:

import { open } from '@open-form/sdk'

const lease = open.form({
  name: 'lease-agreement',
  version: '1.0.0',
  title: 'Residential Lease Agreement',
  parties: {
    landlord: { label: 'Landlord', signature: { required: true } },
    tenant: { label: 'Tenant', signature: { required: true } },
  },
  fields: {
    address: { type: 'address', label: 'Property Address', required: true },
    monthlyRent: { type: 'money', label: 'Monthly Rent', required: true },
    startDate: { type: 'date', label: 'Start Date', required: true },
    endDate: { type: 'date', label: 'End Date', required: true },
  },
})

Add a Layer

Templates define how forms render. Add an inline Markdown template using Handlebars syntax:

const lease = open.form({
  name: 'lease-agreement',
  // ...fields and parties
  defaultLayer: 'markdown',
  layers: {
    markdown: {
      kind: 'inline',
      mimeType: 'text/markdown',
      text: `
# Residential Lease Agreement

**Property:** {{address}}
**Monthly Rent:** {{monthlyRent}}
**Term:** {{startDate}} to {{endDate}}

**Landlord:** {{parties.landlord.fullName}}
**Tenant:** {{parties.tenant.fullName}}
      `,
    },
  },
})

Fill the Form

Use .fill() to create a runtime instance with data:

const filled = lease.fill({
  parties: {
    landlord: { id: 'landlord-1', fullName: 'Jane Smith' },
    tenant: { id: 'tenant-1', fullName: 'John Doe' },
  },
  fields: {
    address: {
      line1: '123 Main St',
      locality: 'Portland',
      region: 'OR',
      postalCode: '97201',
      country: 'USA',
    },
    monthlyRent: { amount: 1500, currency: 'USD' },
    startDate: '2025-02-01',
    endDate: '2026-01-31',
  },
})

Party data requires an id field—this is used for signature tracking.

Render the Form

Render the filled form using the text renderer:

import { textRenderer } from '@open-form/renderer-text'

const output = await filled.render({
  renderer: textRenderer(),
})

console.log(output)

Output:

# Residential Lease Agreement

**Property:** 123 Main St, Portland, OR 97201, USA
**Monthly Rent:** $1,500.00
**Term:** February 1, 2025 to January 31, 2026

**Landlord:** Jane Smith
**Tenant:** John Doe

Fields are automatically serialized—money becomes $1,500.00, address becomes a formatted string.

Save the Artifact

Export the form to JSON or YAML for storage or version control:

const json = lease.toJSON() // or lease.toYAML()
console.log(JSON.stringify(json, null, 2))
{
  "$schema": "https://schemas.open-form.dev/schema.json",
  "kind": "form",
  "name": "lease-agreement",
  "version": "1.0.0",
  "title": "Residential Lease Agreement",
  "fields": {
    "address": { "type": "address", "label": "Property Address", "required": true },
    "monthlyRent": { "type": "money", "label": "Monthly Rent", "required": true },
    "startDate": { "type": "date", "label": "Start Date", "required": true },
    "endDate": { "type": "date", "label": "End Date", "required": true }
  },
  "parties": {
    "landlord": { "label": "Landlord", "signature": { "required": true } },
    "tenant": { "label": "Tenant", "signature": { "required": true } }
  }
}

Load it back with open.load():

const loaded = open.load(jsonString) // works with JSON or YAML

On this page