OpenForm

Quickstart

Last updated on

Create your first form in minutes

Let's build a simple purchase agreement to grasp the basics of OpenForm. We'll define the agreement, populate it with data, validate it, and render the output.

Install the SDK

Let's make a new project and install the SDK.

mkdir quickstart && cd quickstart
npm init -y
npm install @open-form/sdk @open-form/resolvers
mkdir quickstart && cd quickstart
pnpm init -y
pnpm add @open-form/sdk @open-form/resolvers
mkdir quickstart && cd quickstart
yarn init -y
yarn add @open-form/sdk @open-form/resolvers
mkdir quickstart && cd quickstart
bun init -y
bun add @open-form/sdk @open-form/resolvers

Define the Form

Let's define a simple purchase agreement with 2 parties and 3 fields. It will have a single layer that is a Markdown file.

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

const purchaseAgreement = open
  .form()
  .name('purchase-agreement') // required
  .title('Purchase Agreement')
  .version('1.0.0')
  .parties({
    buyer: open.party().label('Buyer').signature({ required: true }),
    seller: open.party().label('Seller').signature({ required: true }),
  })
  .fields({
    quantity: open.field.number().label('Quantity').required(),
    price: open.field.money().label('Price').required(),
    date: open.field.date().label('Date').required(),
  })
  .layers({
    markdown: open.layer().file().mimeType('text/markdown').path('purchase-agreement.md'),
  })
  .defaultLayer('markdown')
  .build()

In this Quickstart, we're using the fluent builder pattern to define the form. You can also use the object pattern if you prefer.

Create the Layer

Next, let's create the layer. We'll use Handlebars syntax to map the form parties/fields to the layer template.

purchase-agreement.md
# Purchase Agreement

{{parties.buyer.fullName}} agrees to purchase {{quantity}} units at {{price}} each.
{{parties.seller.fullName}} agrees to deliver by {{date}}.

OpenForm is agonstic to the rendering engine and file format you choose. You can use any rendering engine and file format you want.

Populate

Let's populate the form with data by using the .fill() method.

const draft = purchaseAgreement.fill({
  fields: {
    quantity: 100,
    price: { amount: 25, currency: 'USD' },
    date: '2025-03-01',
  },
  parties: {
    buyer: { id: 'buyer-1', fullName: 'Alice Johnson' },
    seller: { id: 'seller-1', fullName: 'Bob Smith' },
  },
})

Validate

Next, let's check that the data is valid by using the .isValid() method.

if (draft.isValid()) {
  console.log('Purchase agreement is valid')
} else {
  console.log('Errors:', draft.validate().errors)
}

Render

Let's render the purchase agreement with the data using the .render() method.

import { textRenderer } from '@open-form/sdk'
import { createFsResolver } from '@open-form/resolvers'

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

const output = await draft.render({ renderer, resolver, layer: 'markdown' })

console.log(output)
// Purchase Agreement
//
// Alice Johnson agrees to purchase 100 units at $25.00 each.
// Bob Smith agrees to deliver by March 1, 2025.

Save Artifact

If we want to save our form for future use, we can either save it as a typescript file, or we can serialize it to JSON/YAML for future loading.

import { writeFileSync } from 'node:fs'

writeFileSync('purchase-agreement.json', purchaseAgreement.toJSON())
import { writeFileSync } from 'node:fs'

writeFileSync('purchase-agreement.yaml', purchaseAgreement.toYAML())
{
  "$schema": "https://openform.sh/schemas/2026-01-01.json",
  "kind": "form",
  "name": "purchase-agreement",
  "title": "Purchase Agreement",
  "version": "1.0.0",
  "fields": {
    "quantity": { "type": "number", "label": "Quantity", "required": true },
    "price": { "type": "money", "label": "Price", "required": true },
    "date": { "type": "date", "label": "Date", "required": true }
  },
  "parties": {
    "buyer": { "label": "Buyer", "signature": { "required": true } },
    "seller": { "label": "Seller", "signature": { "required": true } }
  },
  "layers": {
    "text": { "kind": "inline", "mimeType": "text/plain", "text": "..." }
  },
  "defaultLayer": "text"
}
$schema: https://openform.sh/schemas/2026-01-01.json
kind: form
name: purchase-agreement
title: Purchase Agreement
version: 1.0.0
fields:
  quantity:
    type: number
    label: Quantity
    required: true
  price:
    type: money
    label: Price
    required: true
  date:
    type: date
    label: Date
    required: true
parties:
  buyer:
    label: Buyer
    signature:
      required: true
  seller:
    label: Seller
    signature:
      required: true
layers:
  text:
    kind: inline
    mimeType: text/plain
    text: "..."
defaultLayer: text

That's it! You've created a basic form that shows how OpenForm works at design time (defining the schema) and runtime (filling and rendering). As a next step, explore the concepts section or jump into the guides to learn more.

Next Steps

  • Concepts — learn about the core concepts
  • Guides — step-by-step tutorials

On this page