QuickstartLast updated on
Last updated on
Create your first form in minutes
Let's build a simple purchase agreement to grasp the basics of Paradoc. We'll define the agreement, populate it with data, validate it, and render the output.
Install the Packages
Let's make a new project and install the SDK and filesystem resolver. The SDK includes the default renderer.
mkdir quickstart && cd quickstart
npm init -y
npm install @paradoc/sdk @paradoc/resolversThe examples use ES modules and top-level await. Set "type": "module" in package.json, put the code from each step in one index.ts file, and run it with npx tsx index.ts.
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.
The markdown layer is file-backed, so its bytes come from a resolver. Bind
one when you build the form — every instance you derive from it later
(fill, render, and so on) carries it.
import { p } from '@paradoc/sdk'
import { createFsResolver } from '@paradoc/resolvers/fs'
const resolver = createFsResolver({ root: process.cwd() })
const purchaseAgreement = p
.form()
.name('purchase-agreement') // required
.title('Purchase Agreement')
.version('1.0.0')
.parties({
buyer: p.party().label('Buyer').partyType('person').signature({ required: true }),
seller: p.party().label('Seller').partyType('person').signature({ required: true }),
})
.fields({
quantity: p.field.number().label('Quantity').required(),
price: p.field.money().label('Price').required(),
date: p.field.date().label('Date').required(),
})
.layers({
markdown: p.layer().file().mimeType('text/markdown').path('purchase-agreement.md'),
})
.defaultLayer('markdown')
.build({ resolver })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. Paradoc's deterministic template syntax maps the form parties and fields into the layer.
# Purchase Agreement
{{parties.buyer.name}} agrees to purchase {{fields.quantity}} units at {{fields.price}} each.
{{parties.seller.name}} agrees to deliver by {{fields.date}}.Paradoc is agnostic 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: { name: 'Alice Johnson' },
seller: { name: '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. The resolver we bound when building the form travels with draft, so we don't pass it again.
const output = await draft.render({ layer: 'markdown' })
console.log(output)
// # Purchase Agreement
//
// Alice Johnson agrees to purchase 100 units at $25.00 each.
// Bob Smith agrees to deliver by Mar 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', JSON.stringify(purchaseAgreement.toJSON(), null, 2)){
"$schema": "https://schema.paradoc.dev/2026-10-02.json",
"name": "purchase-agreement",
"version": "1.0.0",
"title": "Purchase Agreement",
"metadata": {},
"kind": "form",
"fields": {
"quantity": {
"label": "Quantity",
"required": true,
"type": "number"
},
"price": {
"label": "Price",
"required": true,
"type": "money"
},
"date": {
"label": "Date",
"required": true,
"type": "date"
}
},
"layers": {
"markdown": {
"mimeType": "text/markdown",
"kind": "file",
"path": "purchase-agreement.md"
}
},
"defaultLayer": "markdown",
"parties": {
"buyer": {
"label": "Buyer",
"partyType": "person",
"signature": {
"required": true
}
},
"seller": {
"label": "Seller",
"partyType": "person",
"signature": {
"required": true
}
}
}
}That's it! You've created a basic form that shows how Paradoc 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.